💡

Sitecore Content DeliveryサイトにIdentityServerでアクセス制限をかける方法

2021/08/11に公開

はじめに

本記事ではSitecoreのIdentityServerを利用して、CDサーバーに閲覧制限をかける方法を説明します。開発環境やステージング環境のCDサーバーに対して、何らかの理由でアクセス制限をかけたいときに役立つと思います。

Disclaimer

CDサーバーは多くの場合インターネットに晒されていると思いますので、IdentityServerを使ってアクセス制限をかける場合、当然ながらIdentityServer自体もインターネットに晒す必要があります。これは本来は非推奨です。本番環境でCDサーバーに対して外部IDP(AzureAD B2C等)でログイン制限をかける必要がある場合は、SitecoreのFederated Authenticationという機能を利用して、IdentityServer経由ではなく、CDサーバーから直接IDPと認証を行う方法があります。こちらを参考にしてください。

参考記事
https://kezhan.info/2020/05/20/Sitecore-Website-Federated-Authentication-with-Azure-AD-B2C-with-OpenID-Connect

環境

  • Sitecore Version 10.1 Update 1

手順

  1. IdentityServerにCDサーバー認証設定を追加する
    • identityServer.xmlの変更
    • Sitecore.IdentityServer.Host.xmlの変更
    • IdentityServerをリスタート
  2. CDサーバーに認証設定を追加する
    • ConnectionString.configの変更
    • zzz.EnableWebsiteClientAuth.configを作成
  3. 試す

IdentityServerにCDサーバー認証設定を追加する

CDサーバー用に認証機能を用意するため、IdentityServerに新しいクライアントを定義します。今回はこの新しい認証対象クライアント(CDサーバー)をWebsiteClientと名付けます。以下の2つのファイルにWebsiteClientの定義を追加していきましょう。

  • sitecore/Sitecore.Plugin.IdentityServer/identityServer.xml
  • Sitecore.IdentityServer.Host.xml

identityServer.xml

IdentityServerのsitecore/Sitecore.Plugin.IdentityServer/identityServer.xml<Clients>配下にWebsiteClientを新たに定義します。

<Clients>
...
	<WebsiteClient>
	    <ClientId>WebsiteClient</ClientId>
	    <ClientName>Website Client</ClientName>
	    <AccessTokenType>0</AccessTokenType>
	    <AllowOfflineAccess>true</AllowOfflineAccess>
	    <AlwaysIncludeUserClaimsInIdToken>false</AlwaysIncludeUserClaimsInIdToken>
	    <AccessTokenLifetimeInSeconds>3600</AccessTokenLifetimeInSeconds>
	    <IdentityTokenLifetimeInSeconds>3600</IdentityTokenLifetimeInSeconds>
	    <AllowAccessTokensViaBrowser>true</AllowAccessTokensViaBrowser>
	    <RequireConsent>false</RequireConsent>
	    <RequireClientSecret>false</RequireClientSecret>
	    <AllowedGrantTypes>
		<AllowedGrantType1>hybrid</AllowedGrantType1>
	    </AllowedGrantTypes>
	    <RedirectUris>
		<RedirectUri1>{AllowedCorsOrigin}/identity/signin</RedirectUri1>
		<RedirectUri2>{AllowedCorsOrigin}/signin-oidc</RedirectUri2>
	    </RedirectUris>
	    <PostLogoutRedirectUris>
		<PostLogoutRedirectUri1>{AllowedCorsOrigin}/identity/postexternallogout</PostLogoutRedirectUri1>
		<PostLogoutRedirectUri2>{AllowedCorsOrigin}/signout-callback-oidc</PostLogoutRedirectUri2>
	    </PostLogoutRedirectUris>
	    <AllowedScopes>
		<AllowedScope1>openid</AllowedScope1>
		<AllowedScope2>sitecore.profile</AllowedScope2>
	    </AllowedScopes>
	    <FrontChannelLogoutUris>
		<DefaultFrontChannelLogoutUri>{AllowedCorsOrigin}/FrontChannelLogout</DefaultFrontChannelLogoutUri>
	    </FrontChannelLogoutUris>
	    <FrontChannelLogoutSessionRequired>true</FrontChannelLogoutSessionRequired>
	    <UpdateAccessTokenClaimsOnRefresh>true</UpdateAccessTokenClaimsOnRefresh>
	</WebsiteClient>
...
</Clients>
  • <FrontChannelLogoutUris>の部分に{AllowedCorsOrigin}/FrontChannelLogoutと指定することで、IdentityServerからログアウトしたときに、自動的にクライアントからもログアウトすることができます。このSingle Sign-outの仕組みについては、以下に詳しく載っています。

https://doc.sitecore.com/en/developers/101/sitecore-experience-manager/single-sign-out.html

Sitecore.IdentityServer.Host.xml

Sitecore.IdentityServer.Host.xml<Clients>配下にWebsiteClientを新たに定義します。

<Clients>
...
  <WebsiteClient>
    <AllowedCorsOrigins>
      <AllowedCorsOriginsGroup1>https://YOUR_CD_HOST</AllowedCorsOriginsGroup1>
    </AllowedCorsOrigins>
  </WebsiteClient>
</Clients>
...

IdentityServerをリスタート

設定を反映するためにIdentityServerをリスタートして下さい。
IdentityServer側の設定は以上となります。

CDサーバーに認証設定を追加する

次にCDサーバー側に以下の変更を加えていきます。

  • ConnectionString.configを変更
  • App_Config\Include\zzz.EnableWebsiteClientAuth.configを新たに作成

ConnectionString.config

IdentityServerと通信するためにはIdentityServerと予め共有したSecretをクライアント側が提示する必要があります。CMサーバーにすでにその設定があるので、今回はそれを使います。CMサーバーのConnectionString.configからsitecoreidentity.secretをコピーし、そのままCDサーバー側のConnectionString.configにペーストして下さい。

<?xml version="1.0" encoding="utf-8"?>
<connectionStrings configBuilders="SitecoreConnectionStringsBuilder">
  ...
  <add name="sitecoreidentity.secret" connectionString="YOUR_SECRET_VALUE" />
</connectionStrings>

zzz.EnableWebsiteClientAuth.config

以下のConfigパッチファイルを作成してApp_Config\Include配下に置いて下さい。
例 App_Config\Include\zzz.EnableWebsiteClientAuth.config

<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/" xmlns:set="http://www.sitecore.net/xmlconfig/set/">
   <sitecore role:require="ContentDelivery">
      <sc.variable name="identityServerAuthority" value="https://YOUR_IDSERVER_HOST" />
      <settings>
         <!-- The URI of the IdentityServer provider. -->
         <setting name="FederatedAuthentication.IdentityServer.Authority" value="$(identityServerAuthority)" />
         <!-- The internal authority of SI Server. Leave it empty if it is the same as FederatedAuthentication.IdentityServer.Authority.
           Example: http://sitecore.identity -->
         <!--<setting name="FederatedAuthentication.IdentityServer.InternalAuthority" value="" />-->
         <!-- The client identifier on the IdentityServer. -->
         <setting name="FederatedAuthentication.IdentityServer.ClientId" value="WebsiteClient" />
         <!-- Fill the FederatedAuthentication.IdentityServer.CallbackAuthority setting if you need another host to receive callbacks from IdentityServer. It is useful for reverse proxy configuration. -->
         <!--<setting name="FederatedAuthentication.IdentityServer.CallbackAuthority" value="http://proxy" />-->
         <!-- The client identifier for the Resource Owner Password flow on the IdentityServer. -->
         <setting name="FederatedAuthentication.IdentityServer.ResourceOwnerClientId" value="SitecorePassword" />
         <!-- Wheither HTTPS is required or not for the metadata address or authority -->
         <setting name="FederatedAuthentication.IdentityServer.RequireHttpsMetadata" value="true" />
      </settings>
      <services>
         <configurator type="Sitecore.Owin.Authentication.IdentityServer.ServicesConfigurator, Sitecore.Owin.Authentication.IdentityServer" />
      </services>
      <pipelines>
         <owin.identityProviders>
            <processor type="Sitecore.Owin.Authentication.IdentityServer.Pipelines.IdentityProviders.ConfigureIdentityServer, Sitecore.Owin.Authentication.IdentityServer" resolve="true" id="SitecoreIdentityServer">
               <scopes hint="list">
                  <scope name="openid">openid</scope>
                  <scope name="sitecore.profile">sitecore.profile</scope>
               </scopes>
            </processor>
         </owin.identityProviders>
         <owin.initialize>
            <processor type="Sitecore.Owin.Authentication.IdentityServer.Pipelines.Initialize.InterceptLegacyShellLoginPage, Sitecore.Owin.Authentication.IdentityServer" patch:before="processor[@method='Authenticate']" resolve="true">
               <legacyShellLoginPage>/sitecore/login</legacyShellLoginPage>
            </processor>
            <processor type="Sitecore.Owin.Authentication.IdentityServer.Pipelines.Initialize.JwtBearerAuthentication, Sitecore.Owin.Authentication.IdentityServer" patch:before="processor[@method='Authenticate']" resolve="true">
               <identityProviderName>SitecoreIdentityServer</identityProviderName>
               <audiences hint="raw:AddAudience">
                  <audience value="$(identityServerAuthority)/resources" />
               </audiences>
               <issuers hint="list">
                  <issuer>$(identityServerAuthority)</issuer>
               </issuers>
            </processor>
            <processor type="Sitecore.Owin.Authentication.IdentityServer.Pipelines.Initialize.LogoutEndpoint, Sitecore.Owin.Authentication.IdentityServer" resolve="true" patch:before="processor[@method='Authenticate']" />
         </owin.initialize>
      </pipelines>
      <federatedAuthentication>
         <identityProvidersPerSites>
            <mapEntry name="website" type="Sitecore.Owin.Authentication.Collections.IdentityProvidersPerSitesMapEntry, Sitecore.Owin.Authentication" resolve="true">
               <sites hint="list">
                  <site>website</site>
               </sites>
               <identityProviders hint="list:AddIdentityProvider">
                  <identityProvider ref="federatedAuthentication/identityProviders/identityProvider[@id='SitecoreIdentityServer']" id="SitecoreIdentityServer" />
               </identityProviders>
               <externalUserBuilder type="Sitecore.Owin.Authentication.Services.DefaultExternalUserBuilder, Sitecore.Owin.Authentication" resolve="true">
                  <IsPersistentUser>true</IsPersistentUser>
               </externalUserBuilder>
            </mapEntry>
         </identityProvidersPerSites>
         <identityProviders>
            <identityProvider id="SitecoreIdentityServer" type="Sitecore.Owin.Authentication.IdentityServer.IdentityServerProvider, Sitecore.Owin.Authentication.IdentityServer" resolve="true">
               <caption>Go to login</caption>
               <domain>extranet</domain>
               <enabled>true</enabled>
               <triggerExternalSignOut>true</triggerExternalSignOut>
               <transformations hint="list:AddTransformation">
                  <transformation name="apply additional claims" type="Sitecore.Owin.Authentication.IdentityServer.Transformations.ApplyAdditionalClaims, Sitecore.Owin.Authentication.IdentityServer" resolve="true" />
                  <transformation name="name to long name" type="Sitecore.Owin.Authentication.Services.DefaultTransformation, Sitecore.Owin.Authentication">
                     <sources hint="raw:AddSource">
                        <claim name="name" />
                     </sources>
                     <targets hint="raw:AddTarget">
                        <claim name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" />
                     </targets>
                     <keepSource>true</keepSource>
                  </transformation>
                  <transformation name="role to long role" type="Sitecore.Owin.Authentication.Services.DefaultTransformation, Sitecore.Owin.Authentication">
                     <sources hint="raw:AddSource">
                        <claim name="role" />
                     </sources>
                     <targets hint="raw:AddTarget">
                        <claim name="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" />
                     </targets>
                     <keepSource>false</keepSource>
                  </transformation>
                  <transformation name="set ShadowUser" type="Sitecore.Owin.Authentication.Services.DefaultTransformation, Sitecore.Owin.Authentication">
                     <sources hint="raw:AddSource">
                        <claim name="http://schemas.microsoft.com/identity/claims/identityprovider" value="local" />
                     </sources>
                     <targets hint="raw:AddTarget">
                        <claim name="http://www.sitecore.net/identity/claims/shadowuser" value="true" />
                     </targets>
                     <keepSource>true</keepSource>
                  </transformation>
                  <!-- owin.cookieAuthentication.signIn pipeline uses http://www.sitecore.net/identity/claims/cookieExp claim to override authentication cookie expiration.
                 'exp' claim value can be configured on Sitecore Identity server on the client configuration by IdentityTokenLifetimeInSeconds setting.
                 Note: Claim value is Unix time expressed as the number of seconds that have elapsed since 1970-01-01T00:00:00Z -->
                  <transformation name="use exp claim for authentication cookie expiration" type="Sitecore.Owin.Authentication.Services.DefaultTransformation, Sitecore.Owin.Authentication">
                     <sources hint="raw:AddSource">
                        <claim name="exp" />
                     </sources>
                     <targets hint="raw:AddTarget">
                        <claim name="http://www.sitecore.net/identity/claims/cookieExp" />
                     </targets>
                     <keepSource>true</keepSource>
                  </transformation>
                  <transformation name="remove local role claims" type="Sitecore.Owin.Authentication.IdentityServer.Transformations.RemoveLocalRoles, Sitecore.Owin.Authentication.IdentityServer" />
                  <transformation name="adjust NameIdentifier claim" type="Sitecore.Owin.Authentication.IdentityServer.Transformations.AdjustNameIdentifierClaim, Sitecore.Owin.Authentication.IdentityServer" resolve="true" />
               </transformations>
            </identityProvider>
            <!-- An example of how to add an identity provider as a sub-provider of the Identity Server.
             The 'name' property must be in the following format: SitecoreIdentityServer/[AuthenticationScheme], where the 'AuthenticationScheme' equals the
             authentication scheme of an external identity provider that is configured on the Identity Server.

             Notes:
               1. The 'TriggerExternalSignOut' and 'Transformations' properties are inherited from the the Identity Server provider node and can not be overridden.
               2. To use a sub-provider, the 'Enabled' property of the Identity Server provider must be set to 'Enabled'. -->
            <!--
        <identityProvider id="SitecoreIdentityServer/IdS4-AzureAd" type="Sitecore.Owin.Authentication.Configuration.DefaultIdentityProvider, Sitecore.Owin.Authentication">
          <param desc="name">$(id)</param>
          <param desc="domainManager" type="Sitecore.Abstractions.BaseDomainManager" resolve="true" />
          <caption>Log in with Sitecore Identity: Azure AD</caption>
          <icon>/sitecore/shell/themes/standard/Images/24x24/msazure.png</icon>
          <domain>sitecore</domain>
        </identityProvider>
        -->
         </identityProviders>
         <propertyInitializer>
            <maps>
               <map name="set IsAdministrator" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication" resolve="true">
                  <data hint="raw:AddData">
                     <source name="http://www.sitecore.net/identity/claims/isAdmin" value="true" />
                     <target name="IsAdministrator" value="true" />
                  </data>
               </map>
            </maps>
         </propertyInitializer>
      </federatedAuthentication>
      <sites>
         <site name="website" set:requireLogin="true" set:loginPage="$(loginPath)website/SitecoreIdentityServer" />
      </sites>
   </sitecore>
</configuration>

このパッチファイル自体は既存のSitecore.Owin.Authentication.IdentityServer.configをもとに作られていますが、いくつか変更点があります。

  • roleがContent Deliveryの時にのみ適用
  • FederatedAuthentication.IdentityServer.ClientIdにさっきIdentityServerに定義したClientID WebsiteClient を指定します。これによりIdentityServerがCDをサーバーをWebsiteClientとして認識します。
  • websiteの名で定義されているサイトに対してrequireLoginloginPageという2つの属性を追加します。これによりwebsiteはログイン必須となり、アクセスしようとすると指定されたログインページにRedirectされるようになります。

CDサーバーの設定は以上になります。

試す

では実際にCDサーバーにログイン制限がかかっているか試してみましょう。

  1. まずCMサーバー側でユーザーを作成します。CMサーバーにadmin権限でログインし、ユーザーマネジメントページから新しいユーザーを作成します。ドメインはextranetを指定して下さい。

  2. CMサーバーからログアウトします

  3. 次にCDサーバーへアクセスしてみます。IdentityServerにRedirectされるはずです。作成したユーザーを使ってログインしてみましょう。

  4. ログインが成功するとCDサーバーのトップページが見えるはずです。

  5. CDサーバーからログアウトしてみましょう。ブラウザから直接SIサーバーのURLを入れてSIサーバーのトップページを表示して下さい。右上の方に現在ログインしているユーザーの情報とログアウトリンクが表示されます。ログアウトリンクを押してログアウトページに遷移し、中心にある「Yes」ボタンを押してログアウトできます。

  6. もう一度CDサーバーへアクセスしてみて下さい。シングルサインアウトが成功していれば再度ログインページへRedirectされるはずです。

おわりに

今回は既存のIdentityServerを使ってCDサーバーにアクセス制限をかけてみました。IdentityServerを使うと、コーディングせずにConfigファイルを多少変更するだけで認証設定することができます。本番環境ではこの方法は非推奨となりますが、ステージングや開発環境で一時的にCDサーバーにアクセス制限をかける必要がある場合はお試し下さい。

Discussion