Sitecore Content DeliveryサイトにIdentityServerでアクセス制限をかける方法
はじめに
本記事ではSitecoreのIdentityServerを利用して、CDサーバーに閲覧制限をかける方法を説明します。開発環境やステージング環境のCDサーバーに対して、何らかの理由でアクセス制限をかけたいときに役立つと思います。
Disclaimer
CDサーバーは多くの場合インターネットに晒されていると思いますので、IdentityServerを使ってアクセス制限をかける場合、当然ながらIdentityServer自体もインターネットに晒す必要があります。これは本来は非推奨です。本番環境でCDサーバーに対して外部IDP(AzureAD B2C等)でログイン制限をかける必要がある場合は、SitecoreのFederated Authenticationという機能を利用して、IdentityServer経由ではなく、CDサーバーから直接IDPと認証を行う方法があります。こちらを参考にしてください。
参考記事
環境
- Sitecore Version 10.1 Update 1
手順
- IdentityServerにCDサーバー認証設定を追加する
- identityServer.xmlの変更
- Sitecore.IdentityServer.Host.xmlの変更
- IdentityServerをリスタート
- CDサーバーに認証設定を追加する
- ConnectionString.configの変更
- zzz.EnableWebsiteClientAuth.configを作成
- 試す
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の仕組みについては、以下に詳しく載っています。
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に定義したClientIDWebsiteClient
を指定します。これによりIdentityServerがCDをサーバーをWebsiteClient
として認識します。 -
website
の名で定義されているサイトに対してrequireLogin
、loginPage
という2つの属性を追加します。これによりwebsite
はログイン必須となり、アクセスしようとすると指定されたログインページにRedirectされるようになります。
CDサーバーの設定は以上になります。
試す
では実際にCDサーバーにログイン制限がかかっているか試してみましょう。
-
まずCMサーバー側でユーザーを作成します。CMサーバーにadmin権限でログインし、ユーザーマネジメントページから新しいユーザーを作成します。ドメインはextranetを指定して下さい。
-
CMサーバーからログアウトします
-
次にCDサーバーへアクセスしてみます。IdentityServerにRedirectされるはずです。作成したユーザーを使ってログインしてみましょう。
-
ログインが成功するとCDサーバーのトップページが見えるはずです。
-
CDサーバーからログアウトしてみましょう。ブラウザから直接SIサーバーのURLを入れてSIサーバーのトップページを表示して下さい。右上の方に現在ログインしているユーザーの情報とログアウトリンクが表示されます。ログアウトリンクを押してログアウトページに遷移し、中心にある「Yes」ボタンを押してログアウトできます。
-
もう一度CDサーバーへアクセスしてみて下さい。シングルサインアウトが成功していれば再度ログインページへRedirectされるはずです。
おわりに
今回は既存のIdentityServerを使ってCDサーバーにアクセス制限をかけてみました。IdentityServerを使うと、コーディングせずにConfigファイルを多少変更するだけで認証設定することができます。本番環境ではこの方法は非推奨となりますが、ステージングや開発環境で一時的にCDサーバーにアクセス制限をかける必要がある場合はお試し下さい。
Discussion