認証と承認
Note
この電子ブックは 2017 年春に発行されたもので、その後は改訂されていません。 このブックには今なお価値のある内容が多く含まれていますが、一部の記載内容は古くなっています。
認証とは、名前とパスワードなどの ID 資格情報をユーザーから取得し、その資格情報を機関に対して検証するプロセスです。 資格情報が有効な場合、資格情報を送信したエンティティは認証済みの ID と見なされます。 ID が認証されると、承認プロセスによって、その ID に特定のリソースへのアクセス権があるかどうかが判断されます。
ASP.NET MVC Web アプリケーションと通信する Xamarin.Forms アプリに認証と承認を統合するには、ASP.NET Core Identity、外部認証プロバイダー (Microsoft、Google、Facebook、Twitter など)、認証ミドルウェアの使用など、多数の方法があります。 eShopOnContainers モバイル アプリは、IdentityServer 4 を使うコンテナー化された ID マイクロサービスで、認証と承認を実行します。 このモバイル アプリは、ユーザーの認証またはリソースへのアクセスのため、IdentityServer にセキュリティ トークンを要求します。 IdentityServer がユーザーの代わりにトークンを発行するには、ユーザーが IdentityServer にサインインする必要があります。 ただし、IdentityServer は、認証用のユーザー インターフェイスまたはデータベースを備えていません。 したがって、eShopOnContainers 参照アプリケーションでは、ASP.NET Core Identity をこの目的に使います。
認証
アプリケーションが現在のユーザーの ID を認識する必要がある場合は、認証が必要です。 ユーザーを識別するための ASP.NET Core の主要なメカニズムは、開発者が構成したデータ ストアにユーザー情報を格納する ASP.NET Core Identity メンバーシップ システムです。 通常、このデータ ストアは EntityFramework ストアですが、カスタム ストアまたはサード パーティのパッケージを使って、Azure Storage、Azure Cosmos DB、またはその他の場所に ID 情報を保存できます。
ローカル ユーザーのデータストアを利用する認証シナリオや、Cookie を使って要求間で ID 情報を保持する認証シナリオ (ASP.NET MVC Web アプリケーションでは一般的) では、ASP.NET Core Identity が適切なソリューションです。 ただし、Cookie は、データを保持して送信する自然な手段とは限りません。 たとえば、モバイル アプリからアクセスされる RESTful エンドポイントを公開する ASP.NET Core Web アプリケーションでは、このシナリオでは Cookie を使用できないため、通常はベアラー トークン認証を使用する必要があります。 ただし、ベアラー トークンは簡単に取得でき、モバイル アプリからの Web 要求の承認ヘッダーに含めることができます。
IdentityServer 4 を使用したベアラー トークンの発行
IdentityServer 4 は、ASP.NET Core 用のオープンソースの OpenID Connect および OAuth 2.0 フレームワークであり、ASP.NET Core Identity のローカル ユーザーに対するセキュリティ トークンの発行など、多くの認証と承認のシナリオで使用できます。
Note
OpenID Connect と OAuth 2.0 は非常に似ていますが、役割は異なります。
OpenID Connect は、OAuth 2.0 プロトコルの上位の認証レイヤーです。 OAuth 2 は、アプリケーションがセキュリティ トークン サービスにアクセス トークンを要求し、それらを使って API と通信できるようにするプロトコルです。 この委任により、認証と承認を一元化できるため、クライアント アプリケーションと API 両方の複雑さが軽減されます。
OpenID Connect と OAuth 2.0 の組み合わせは、認証と API アクセスという 2 つの基本的なセキュリティの問題を組み合わせたものであり、IdentityServer 4 はこれらのプロトコルの実装です。
eShopOnContainers 参照アプリケーションなど、クライアントからマイクロサービスへの直接通信を使うアプリケーションでは、図 9-1 に示すように、セキュリティ トークン サービス (STS) として機能する専用の認証マイクロサービスを使って、ユーザーを認証できます。 クライアントとマイクロサービスの直接通信について詳しくは、「クライアントとマイクロサービスの間の通信」をご覧ください。
図 9-1: 専用の認証マイクロサービスによる認証
eShopOnContainers モバイル アプリは ID マイクロサービスと通信し、IdentityServer 4 を使用して認証と API のアクセス制御を実行します。 したがって、このモバイル アプリは、ユーザーの認証またはリソースへのアクセスのために IdentityServer にトークンを要求します。
- IdentityServer でのユーザーの認証は、ID トークンを要求するモバイル アプリによって実行され、認証プロセスの結果が示されます。 少なくとも、それにはユーザーの識別子と、ユーザーがいつどのように認証されたかに関する情報が含まれます。 また、追加の ID データが含まれる場合もあります。
- IdentityServer でのリソースへのアクセスは、"アクセス" トークンを要求するモバイル アプリによって実行され、API リソースへのアクセスが許可されます。 クライアントはアクセス トークンを要求して、API にそれを転送します。 アクセス トークンには、クライアントおよびユーザー (存在する場合) に関する情報が含まれます。 その後、API はその情報を使ってデータへのアクセスを承認します。
Note
クライアントがトークンを要求するには、事前に IdentityServer に登録する必要があります。
Web アプリケーションへの IdentityServer の追加
ASP.NET Core Web アプリケーションで IdentityServer 4 を使うには、それを Web アプリケーションの Visual Studio ソリューションに追加する必要があります。 詳しくは、IdentityServer ドキュメントの概要に関する説明をご覧ください。
IdentityServer を Web アプリケーションの Visual Studio ソリューションに含めたら、OpenID Connect および OAuth 2.0 エンドポイントへの要求を処理できるように、それを Web アプリケーションの HTTP 要求処理パイプラインに追加する必要があります。 これは、次のコード例で示すように、Web アプリケーションの Startup
クラスの Configure
メソッドで実現されます。
public void Configure(
IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
...
app.UseIdentity();
...
}
Web アプリケーションの HTTP 要求処理パイプラインでは順序が重要です。 そのため、IdentityServer は、ログイン画面を実装する UI フレームワークの前のパイプラインに追加する必要があります。
IdentityServer の構成
eShopOnContainers 参照アプリケーションの次のコード例に示されているように、Web アプリケーションの Startup
クラスの ConfigureServices
メソッドで services.AddIdentityServer
メソッドを呼び出して、IdentityServer を構成する必要があります。
public void ConfigureServices(IServiceCollection services)
{
...
services.AddIdentityServer(x => x.IssuerUri = "null")
.AddSigningCredential(Certificate.Get())
.AddAspNetIdentity<ApplicationUser>()
.AddConfigurationStore(builder =>
builder.UseSqlServer(connectionString, options =>
options.MigrationsAssembly(migrationsAssembly)))
.AddOperationalStore(builder =>
builder.UseSqlServer(connectionString, options =>
options.MigrationsAssembly(migrationsAssembly)))
.Services.AddTransient<IProfileService, ProfileService>();
}
services.AddIdentityServer
メソッドを呼び出した後、追加の Fluent API を呼び出して次の構成を行います。
- 署名に使われる資格情報。
- ユーザーがアクセスを要求する可能性がある API と ID リソース。
- トークンを要求するために接続するクライアント。
- ASP.NET Core Identity。
ヒント
IdentityServer 4 の構成を動的に読み込みます。 IdentityServer 4 の API を使うと、構成オブジェクトのメモリ内リストから IdentityServer を構成できます。 eShopOnContainers 参照アプリケーションでは、これらのメモリ内コレクションはアプリケーションにハードコーディングされています。 ただし、運用シナリオでは、構成ファイルまたはデータベースから動的に読み込むことができます。
ASP.NET Core Identity を使うように IdentityServer を構成する方法については、IdentityServer のドキュメントの「ASP.NET Core Identity の使用」をご覧ください。
API リソースの構成
API リソースを構成するとき、AddInMemoryApiResources
メソッドは IEnumerable<ApiResource>
コレクションを必要とします。 次に示すコード例は、eShopOnContainers 参照アプリケーションでこのコレクションを提供する GetApis
メソッドです。
public static IEnumerable<ApiResource> GetApis()
{
return new List<ApiResource>
{
new ApiResource("orders", "Orders Service"),
new ApiResource("basket", "Basket Service")
};
}
このメソッドでは、IdentityServer が orders API と basket API を保護する必要があることが指定されています。 そのため、これらの API を呼び出すときは、IdentityServer で管理されるアクセス トークンが必要になります。 ApiResource
型について詳しくは、IdentityServer 4 のドキュメントの「API リソース」をご覧ください。
ID リソースの構成
ID リソースを構成するとき、AddInMemoryIdentityResources
メソッドは IEnumerable<IdentityResource>
コレクションを必要とします。 ID リソースは、ユーザー ID、名前、メール アドレスなどのデータです。 各 ID リソースには一意の名前があり、それには任意のクレームの種類を割り当てることができ、それはその後ユーザーの ID トークンに組み込まれます。 次に示すコード例は、eShopOnContainers 参照アプリケーションでこのコレクションを提供する GetResources
メソッドです。
public static IEnumerable<IdentityResource> GetResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile()
};
}
OpenID Connect の仕様では、いくつかの標準 ID リソースが指定されています。 最小要件は、ユーザーに一意の ID を生成するためのサポートが提供されていることです。 これは、IdentityResources.OpenId
ID リソースを公開することによって実現されます。
Note
IdentityResources
クラスでは、OpenID Connect の仕様で定義されているすべてのスコープ (OpenID、メール、プロファイル、電話、アドレス) がサポートされます。
IdentityServer では、カスタム ID リソースの定義もサポートされています。 IdentityResource
型について詳しくは、IdentityServer 4 のドキュメントの「ID リソース」をご覧ください。
クライアントの構成
クライアントは、IdentityServer にトークンを要求できるアプリケーションです。 通常、少なくとも次の設定がクライアントごとに定義されている必要があります。
- 一意のクライアント ID。
- トークン サービスとの許可される相互作用 (許可の種類と呼ばれます)。
- ID トークンとアクセス トークンの送信先の場所 (リダイレクト URI と呼ばれます)。
- クライアントがアクセスを許可されているリソースのリスト (スコープと呼ばれます)。
クライアントを構成するとき、AddInMemoryClients
メソッドは IEnumerable<Client>
コレクションを必要とします。 次に示すコード例は、eShopOnContainers 参照アプリケーションでこのコレクションを提供する GetClients
メソッドでの eShopOnContainers モバイル アプリの構成です。
public static IEnumerable<Client> GetClients(Dictionary<string,string> clientsUrl)
{
return new List<Client>
{
...
new Client
{
ClientId = "xamarin",
ClientName = "eShop Xamarin OpenId Client",
AllowedGrantTypes = GrantTypes.Hybrid,
ClientSecrets =
{
new Secret("secret".Sha256())
},
RedirectUris = { clientsUrl["Xamarin"] },
RequireConsent = false,
RequirePkce = true,
PostLogoutRedirectUris = { $"{clientsUrl["Xamarin"]}/Account/Redirecting" },
AllowedCorsOrigins = { "http://eshopxamarin" },
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.OfflineAccess,
"orders",
"basket"
},
AllowOfflineAccess = true,
AllowAccessTokensViaBrowser = true
},
...
};
}
この構成では、次のプロパティのデータを指定しています。
ClientId
: クライアントの一意の ID。ClientName
: クライアントの表示名。ログと同意画面に使用されます。AllowedGrantTypes
: クライアントが IdentityServer と対話する方法を指定します。 詳しくは、「認証フローの構成」をご覧ください。ClientSecrets
: トークン エンドポイントにトークンを要求するときに使われるクライアント シークレット資格情報を指定します。RedirectUris
: トークンまたは承認コードを返す先の許可された URI を指定します。RequireConsent
: 同意画面が必要かどうかを指定します。RequirePkce
: 承認コードを使用するクライアントが証明キーを送信する必要があるかどうかを指定します。PostLogoutRedirectUris
: ログアウト後にリダイレクトする先の許可された URI を指定します。AllowedCorsOrigins
: IdentityServer が呼び出し元からのクロスオリジン呼び出しを許可できるように、クライアントの呼び出し元を指定します。AllowedScopes
: クライアントがアクセス権を持っているリソースを指定します。 既定では、クライアントはどのリソースに対するアクセス権も持っていません。AllowOfflineAccess
: クライアントが更新トークンを要求できるかどうかを指定します。
認証フローの構成
クライアントと IdentityServer の間の認証フローは、Client.AllowedGrantTypes
プロパティで許可の種類を指定することにより構成できます。 OpenID Connect と OAuth 2.0 の仕様では、次のような多くの認証フローが定義されています。
- 暗黙的。 このフローはブラウザー ベースのアプリケーションに最適化されており、ユーザー認証専用または認証とアクセス トークン要求に使用する必要があります。 すべてのトークンはブラウザー経由で送信されるため、更新トークンなどの高度な機能は許可されません。
- 認証コード。 このフローでは、ブラウザー フロント チャネルとは異なり、バック チャネルでトークンを取得でき、クライアント認証をサポートする機能も提供されます。
- ハイブリッド。 このフローは、暗黙的な種類と、承認コード許可の種類の組み合わせです。 ID トークンはブラウザー チャネル経由で送信され、認証コードなどの他の成果物とともに署名されたプロトコル応答が含まれます。 応答の検証が成功したら、バック チャネルを使ってアクセス トークンと更新トークンを取得する必要があります。
ヒント
ハイブリッド認証フローを使用します。 ハイブリッド認証フローは、ブラウザー チャネルに対して行われる多くの攻撃を軽減し、アクセス トークン (および場合によっては更新トークン) を取得するネイティブ アプリケーションに推奨されるフローです。
認証フローについて詳しくは、IdentityServer 4 のドキュメントの許可の種類に関する説明をご覧ください。
認証の実行
IdentityServer がユーザーの代わりにトークンを発行するには、ユーザーが IdentityServer にサインインする必要があります。 ただし、IdentityServer は、認証用のユーザー インターフェイスまたはデータベースを備えていません。 したがって、eShopOnContainers 参照アプリケーションでは、ASP.NET Core Identity をこの目的に使います。
eShopOnContainers モバイル アプリは、ハイブリッド認証フローを使って IdentityServer で認証を行います。図 9-2 はこれを示したものです。
図 9-2: サインイン プロセスの概要
<base endpoint>:5105/connect/authorize
に対してサインイン要求が行われます。 認証が成功すると、IdentityServer は認証コードと ID トークンを含む認証応答を返します。 その後、承認コードが <base endpoint>:5105/connect/token
に送信され、アクセス トークン、ID トークン、更新トークンで応答します。
eShopOnContainers モバイル アプリを IdentityServer からサインアウトするには、追加のパラメータを指定して <base endpoint>:5105/connect/endsession
に要求を送信します。 サインアウトが発生すると、IdentityServer はログアウト後のリダイレクト URI をモバイル アプリに送信して応答します。 このプロセスを図 9-3 に示します。
図 9-3: サインアウト プロセスの概要
eShopOnContainers モバイル アプリでは、IdentityServer との通信は、IIdentityService
インターフェイスを実装する IdentityService
クラスによって実行されます。 このインターフェイスでは、実装するクラスは CreateAuthorizationRequest
、CreateLogoutRequest
、GetTokenAsync
メソッドを提供する必要があることが指定されています。
サインイン
ユーザーが LoginView
の [LOGIN] ボタンをタップすると、LoginViewModel
クラスの SignInCommand
が実行され、それにより SignInAsync
メソッドが実行されます。 以下のコード例はこのメソッドを示しています。
private async Task SignInAsync()
{
...
LoginUrl = _identityService.CreateAuthorizationRequest();
IsLogin = true;
...
}
このメソッドは、IdentityService
クラスの CreateAuthorizationRequest
メソッドを呼び出します。これを次のコード例に示します。
public string CreateAuthorizationRequest()
{
// Create URI to authorization endpoint
var authorizeRequest = new AuthorizeRequest(GlobalSetting.Instance.IdentityEndpoint);
// Dictionary with values for the authorize request
var dic = new Dictionary<string, string>();
dic.Add("client_id", GlobalSetting.Instance.ClientId);
dic.Add("client_secret", GlobalSetting.Instance.ClientSecret);
dic.Add("response_type", "code id_token");
dic.Add("scope", "openid profile basket orders locations marketing offline_access");
dic.Add("redirect_uri", GlobalSetting.Instance.Callback);
dic.Add("nonce", Guid.NewGuid().ToString("N"));
dic.Add("code_challenge", CreateCodeChallenge());
dic.Add("code_challenge_method", "S256");
// Add CSRF token to protect against cross-site request forgery attacks.
var currentCSRFToken = Guid.NewGuid().ToString("N");
dic.Add("state", currentCSRFToken);
var authorizeUri = authorizeRequest.Create(dic);
return authorizeUri;
}
このメソッドは、必要なパラメータを使って、IdentityServer の承認エンドポイントの URI を作成します。 承認エンドポイントは、ユーザー設定として公開される基本エンドポイントのポート 5105 上の /connect/authorize
にあります。 ユーザー設定について詳しくは、「構成管理」をご覧ください。
Note
eShopOnContainers モバイル アプリの攻撃対象領域は、Proof Key for Code Exchange (PKCE) 拡張機能を OAuth に実装すると減らすことができます。 PKCE は、認証コードがインターセプトされた場合に使用されないように保護します。 これは、クライアントがシークレット検証子を生成し、そのハッシュが承認要求で渡され、承認コードを引き換えるときにハッシュされていない状態で提示されることによって、実現されます。 PKCE について詳しくは、Internet Engineering Task Force の Web サイトの「OAuth パブリック クライアントによる Proof Key for Code Exchange」をご覧ください。
返された URI は、LoginViewModel
クラスの LoginUrl
プロパティに格納されます。 IsLogin
プロパティが true
になると、LoginView
の WebView
が表示されるようになります。 データWebView
は、そのSource
プロパティLoginUrl
LoginUrl
をクラスのプロパティにバインドします。これにより、プロパティが "Identity Server" 認証エンドポイントに設定されている場合に、"IdentityServer にサインイン" 要求が行われます。 LoginViewModel
IdentityServer がこの要求を受信し、ユーザーが認証されていない場合、WebView
は構成済みのログイン ページにリダイレクトされます。図 9-4 はこれを示したものです。
図 9-4: WebView によって表示されるログイン ページ
ログインが完了すると、WebView
は戻り先 URI にリダイレクトされます。 この WebView
のナビゲーションにより、次のコード例で示すように、LoginViewModel
クラスの NavigateAsync
メソッドが実行されます。
private async Task NavigateAsync(string url)
{
...
var authResponse = new AuthorizeResponse(url);
if (!string.IsNullOrWhiteSpace(authResponse.Code))
{
var userToken = await _identityService.GetTokenAsync(authResponse.Code);
string accessToken = userToken.AccessToken;
if (!string.IsNullOrWhiteSpace(accessToken))
{
Settings.AuthAccessToken = accessToken;
Settings.AuthIdToken = authResponse.IdentityToken;
await NavigationService.NavigateToAsync<MainViewModel>();
await NavigationService.RemoveLastFromBackStackAsync();
}
}
...
}
このメソッドは、戻り先 URI に含まれる認証応答を解析し、有効な承認コードが存在する場合は、IdentityServer のトークン エンドポイントに要求を行って、承認コード、PKCE シークレット検証子、その他の必要なパラメータを渡します。 トークン エンドポイントは、ユーザー設定として公開される基本エンドポイントのポート 5105 上の /connect/token
にあります。 ユーザー設定について詳しくは、「構成管理」をご覧ください。
ヒント
戻り先 URI を検証します。 eShopOnContainers モバイル アプリでは戻り先 URI の検証は行われませんが、オープン リダイレクト攻撃を防ぐため、戻り先 URI が既知の場所を参照していることを検証するのがベスト プラクティスです。
トークン エンドポイントは、有効な承認コードと PKCE シークレット検証子を受け取ると、アクセス トークン、ID トークン、更新トークンで応答します。 アクセス トークン (API リソースへのアクセスを許可する) と ID トークンは、その後アプリケーションの設定として保存され、ページ ナビゲーションが実行されます。 したがって、eShopOnContainers モバイル アプリでの全体的な影響は次のようになります。ユーザーが IdentityServer で正常に認証できる場合、ユーザーは MainView
ページにナビゲートされ、それは選択されたタブとして CatalogView
を表示する TabbedPage
です。
ページのナビゲーションについては、「ナビゲーション」をご覧ください。 WebView
のナビゲーションによってビュー モデルのメソッドがどのように実行されるのかについては、「ビヘイビアーを使用したナビゲーションの呼び出し」をご覧ください。 アプリケーションの設定については、「構成管理」をご覧ください。
Note
SettingsView
でモック サービスを使うようにアプリが構成されている場合、eShopOnContainers はモック サインインも許可します。 このモードでは、アプリは IdentityServer と通信せず、代わりにユーザーが任意の資格情報を使用してサインインするのを許可します。
サインアウト
ユーザーが ProfileView
の [LOG OUT] ボタンをタップすると、ProfileViewModel
クラスの LogoutCommand
が実行され、それにより LogoutAsync
メソッドが実行されます。 このメソッドは、LoginView
ページへのページ ナビゲーションを実行し、true
に設定された LogoutParameter
のインスタンスをパラメーターとして渡します。 ページ ナビゲーション中にパラメーターを渡す方法の詳細については、「ナビゲーション中にパラメーターを渡す」を参照してください。
ビューが作成され、そこに移動すると、ビューに関連付けられているビュー モデルの InitializeAsync
メソッドが実行され、LoginViewModel
クラスの Logout
メソッドが実行されます。次に示すのはそのコード例です。
private void Logout()
{
var authIdToken = Settings.AuthIdToken;
var logoutRequest = _identityService.CreateLogoutRequest(authIdToken);
if (!string.IsNullOrEmpty(logoutRequest))
{
// Logout
LoginUrl = logoutRequest;
}
...
}
このメソッドは、IdentityService
クラスの CreateLogoutRequest
メソッドを呼び出し、アプリケーションの設定から取得した ID トークンをパラメーターとして渡します。 アプリケーションの設定について詳しくは、「構成管理」をご覧ください。 次のコード例は、CreateLogoutRequest
メソッドを示しています。
public string CreateLogoutRequest(string token)
{
...
return string.Format("{0}?id_token_hint={1}&post_logout_redirect_uri={2}",
GlobalSetting.Instance.LogoutEndpoint,
token,
GlobalSetting.Instance.LogoutCallback);
}
このメソッドは、必要なパラメータを使って、IdentityServer のセッション終了エンドポイントへの URI を作成します。 セッション終了エンドポイントは、ユーザー設定として公開される基本エンドポイントのポート 5105 上の /connect/endsession
にあります。 ユーザー設定について詳しくは、「構成管理」をご覧ください。
返された URI は、LoginViewModel
クラスの LoginUrl
プロパティに格納されます。 IsLogin
プロパティが true
の場合は、LoginView
の WebView
が表示されます。 WebView
データは、その Source
プロパティを LoginViewModel
クラスの LoginUrl
プロパティにバインドするため、LoginUrl
プロパティが IdentityServer のセッション終了エンドポイントに設定されていると、IdentityServer にサインアウト要求を行います。 IdentityServer がこの要求を受信すると、ユーザーがサインインしていれば、サインアウトが発生します。 認証は、ASP.NET Core の Cookie 認証ミドルウェアによって管理される Cookie を使って追跡されます。 そのため、IdentityServer からサインアウトすると、認証 Cookie は削除され、ログアウト後リダイレクト URI がクライアントに返送されます。
モバイル アプリでは、WebView
はログアウト後リダイレクト URI にリダイレクトされます。 この WebView
のナビゲーションにより、次のコード例で示すように、LoginViewModel
クラスの NavigateAsync
メソッドが実行されます。
private async Task NavigateAsync(string url)
{
...
Settings.AuthAccessToken = string.Empty;
Settings.AuthIdToken = string.Empty;
IsLogin = false;
LoginUrl = _identityService.CreateAuthorizationRequest();
...
}
このメソッドは、ID トークンとアクセス トークンの両方をアプリケーション設定からクリアし、IsLogin
プロパティを false
に設定します。これにより、LoginView
ページの WebView
が非表示になります。 最後に、ユーザーが次にサインインを始めるときに備えて、LoginUrl
プロパティには、必要なパラメーターを使って IdentityServer の承認エンドポイントの URI が設定されます。
ページのナビゲーションについては、「ナビゲーション」をご覧ください。 WebView
のナビゲーションによってビュー モデルのメソッドがどのように実行されるのかについては、「ビヘイビアーを使用したナビゲーションの呼び出し」をご覧ください。 アプリケーションの設定については、「構成管理」をご覧ください。
Note
SettingsView でモック サービスを使うようにアプリが構成されている場合、eShopOnContainers はモック サインアウトも許可します。 このモードでは、アプリは IdentityServer と通信せず、アプリケーションの設定から格納されているトークンをクリアします。
承認
認証の後、ASP.NET Core Web API は多くの場合、アクセスを承認する必要があります。これにより、一部の認証されたユーザーはサービスで API を使用できるようになりますが、すべてのユーザーではありません。
ASP.NET Core MVC ルートへのアクセスを制限するには、次のコード例で示すように、コントローラーまたはアクションに Authorize 属性を適用します。これにより、コントローラーまたはアクションへのアクセスが、認証されたユーザーに制限されます。
[Authorize]
public class BasketController : Controller
{
...
}
承認されていないユーザーが Authorize
属性でマークされたコントローラーまたはアクションにアクセスしようとすると、MVC フレームワークは 401 (未承認) HTTP 状態コードを返します。
Note
Authorize
属性でパラメータを指定して、特定のユーザーに API を制限できます。 詳細については、「承認」をご覧ください。
IdentityServer を承認ワークフローに統合し、それによって提供されるアクセス トークンで承認を制御することができます。 この方法を図 9-5 に示します。
図 9-5: アクセス トークンによる承認
eShopOnContainers モバイル アプリは、ID マイクロサービスと通信し、認証プロセスの一部としてアクセス トークンを要求します。 その後、アクセス トークンは、アクセス要求の一部として、注文マイクロサービスとバスケット マイクロサービスによって公開される API に転送されます。 アクセス トークンには、クライアントとユーザーに関する情報が含まれます。 その後、API はその情報を使ってデータへのアクセスを承認します。 API を保護するように IdentityServer を構成する方法については、「API リソースの構成」をご覧ください。
承認を実行するように IdentityServer を構成する
IdentityServer で承認を実行するには、その承認ミドルウェアを Web アプリケーションの HTTP 要求パイプラインに追加する必要があります。 そのミドルウェアは、Web アプリケーションの Startup
クラスの ConfigureAuth
メソッドに追加され、Configure
メソッドから呼び出されます。eShopOnContainers 参照アプリケーションの次のコード例は、これを示したものです。
protected virtual void ConfigureAuth(IApplicationBuilder app)
{
var identityUrl = Configuration.GetValue<string>("IdentityUrl");
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = identityUrl.ToString(),
ScopeName = "basket",
RequireHttpsMetadata = false
});
}
このメソッドを使うと、有効なアクセス トークンでのみ API にアクセスできることが保証されます。 ミドルウェアは、受信したトークンを検証して、それが信頼された発行元から送信されていることを確認し、トークンを受け取った API でそれを使用できることを検証します。 したがって、注文またはバスケット コントローラーを参照すると、アクセス トークンが必要であることを示す401 (未承認) HTTP 状態コードが返されます。
Note
app.UseMvc()
または app.UseMvcWithDefaultRoute()
で MVC を追加する前に、IdentityServer の承認ミドルウェアを Web アプリケーションの HTTP 要求パイプラインに追加する必要があります。
API にアクセス要求を行う
注文およびバスケット マイクロサービスに要求を行うときは、次のコード例に示すように、認証プロセスの間に IdentityServer から取得したアクセス トークンを、要求に含める必要があります。
var authToken = Settings.AuthAccessToken;
Order = await _ordersService.GetOrderAsync(Convert.ToInt32(order.OrderNumber), authToken);
アクセス トークンはアプリケーションの設定として格納され、プラットフォーム固有のストレージから取得されて、OrderService
クラスの GetOrderAsync
メソッドの呼び出しに追加されます。
同様に、次のコードのように、IdentityServer で保護された API にデータを送信するときも、アクセス トークンを含める必要があります。
var authToken = Settings.AuthAccessToken;
await _basketService.UpdateBasketAsync(new CustomerBasket
{
BuyerId = userInfo.UserId,
Items = BasketItems.ToList()
}, authToken);
アクセス トークンはプラットフォーム固有のストレージから取得され、BasketService
クラスの UpdateBasketAsync
メソッドの呼び出しに含まれます。
eShopOnContainers モバイル アプリの RequestProvider
クラスは、HttpClient
クラスを使って、eShopOnContainers 参照アプリケーションによって公開されている RESTful API への要求を行います。 承認が必要な注文 API とバスケット API に要求を行うときは、有効なアクセス トークンを要求に含める必要があります。 これは、次のコード例で示すように、HttpClient
インスタンスのヘッダーにアクセス トークンを追加して実現できます。
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
各要求で送信されるヘッダーは HttpClient
クラスの DefaultRequestHeaders
プロパティで公開されており、アクセス トークンは文字列 Authorization
を前に付けて Bearer
ヘッダーに追加されます。 要求が RESTful API に送信されると、Authorization
ヘッダーの値が抽出されて、それが信頼された発行者から送信されていることを確認するために検証され、それを受信した API を呼び出すアクセス許可がユーザーにあるかどうかを判断するために使われます。
eShopOnContainers モバイル アプリが Web 要求を行う方法について詳しくは、「リモート データへのアクセス」をご覧ください。
まとめ
ASP.NET MVC Web アプリケーションと通信する Xamarin.Forms アプリに認証と認可を統合するには、多くの方法があります。 eShopOnContainers モバイル アプリは、IdentityServer 4 を使うコンテナー化された ID マイクロサービスで、認証と承認を実行します。 IdentityServer は、ASP.NET Core Identity と統合してベアラー トークン認証を実行する ASP.NET Core 用の、オープンソースの OpenID Connect および OAuth 2.0 フレームワークです。
このモバイル アプリは、ユーザーの認証またはリソースへのアクセスのため、IdentityServer にセキュリティ トークンを要求します。 リソースにアクセスするときは、承認を必要とする API への要求にアクセス トークンを含める必要があります。 IdentityServer のミドルウェアは、受信したアクセス トークンを検証し、それらが信頼された発行元から送信されていること、およびそれらを受け取った API で使用できることを確認します。