2018년 3월 26일 월요일

사이트코어 Federated Authentication 설정 및 사용하기

이번에는 사이트코어 9에서 소개된 Federated Authentication (FA)에 대하여 알아보도록 하자.

사이트코어는 .NET 프레임워크를 베이스로 사용하여 플랫폼을 윈도우 환경에 설치한다. 많은 유저 및 컴퍼니들은 CMS 사용자 로그인 프로세스를 Active Directory (AD)와 연동하여 사용하고 있으며, 대표적인 모듈로는 사이트코어에서 제공하는 Activie Direcotry 모듈이 있다.
이는 AD 도메인을 사이트코어 플랫폼과 연동시킨다. 기존 AD에 세팅되는 모든 유저의 Role 및 Permission을 똑같이 사이트코어에 적용하여 추가적인 어카운트 세팅없이 AD에 account가 존재하다면, 똑같이 유저정보로 사이트코어 CMS에 로그인할수있다.

사이트코어 9에서는 다양한 Authentication Provider를 제공한다. 가장 대포적인 예가 ADFS를 통하여 사이트코어를 로그인 하는 방법이다. 이는 기본적으로 AD On-Perm을 Azure Activie Directory Connect synchronization을 통하여 AzureAD와 연동을 하고, AzureAD에 Sync된 정보를 Microsoft의 Owin 미들웨어를 통하여 Token 기반의 OpenID Connect를 사용한다.

지금부터 Federated Authentication을 세팅 및 설정하는 방법에 대하여 알아보도록 하자.
  1. 사이트코어에서 FA를 사용하기 위해서는 "/wwwroot/App_Config/Include/Examples"폴더에 "Sitecore.Owin.Authentication.Enabler.config" 파일을 Include 폴더에 복사하고, "FederatedAuthentication.Enabled"을 "true"로 설정을 한다.
  2. 새로운 class를 만들어 IdentityProvider를 설정한다.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    using Microsoft.Owin.Security.OpenIdConnect;
    using Owin;
    using Sitecore.Configuration;
    using Sitecore.Diagnostics;
    using Sitecore.Owin.Authentication.Configuration;
    using Sitecore.Owin.Authentication.Pipelines.IdentityProviders;
    using Sitecore.Owin.Authentication.Services;
    using System.Globalization;
    using System.Security.Claims;
    using System.Threading.Tasks;
    using Microsoft.Owin.Security;
    
    namespace MyProject.Sitecore.Owin.AzureAD.Pipelines
    {
        public class CustomAzureADIdentityProvider : IdentityProvidersProcessor
        {
    
            public CustomAzureADIdentityProvider(FederatedAuthenticationConfiguration federatedAuthenticationConfiguration) : base(federatedAuthenticationConfiguration)
            {
            }
    
            protected override string IdentityProviderName => "AzureAd";
    
            protected override void ProcessCore(IdentityProvidersArgs args)
            {
                Assert.ArgumentNotNull(args, nameof(args));
    
                var identityProvider = this.GetIdentityProvider();
                var authenticationType = this.GetAuthenticationType();
    
                string aadInstance = Settings.GetSetting("AADInstance");
                string tenant = Settings.GetSetting("Tenant");
                string clientId = Settings.GetSetting("ClientId");
                string postLogoutRedirectURI = Settings.GetSetting("PostLogoutRedirectURI");
                string redirectURI = Settings.GetSetting("RedirectURI");
    
                string authority = string.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
    
                args.App.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
                {
                    Caption = identityProvider.Caption,
                    AuthenticationType = authenticationType,
                    AuthenticationMode = AuthenticationMode.Passive,
                    ClientId = clientId,
                    Authority = authority,
                    PostLogoutRedirectUri = postLogoutRedirectURI,
                    RedirectUri = redirectURI,
                    
                    Notifications = new OpenIdConnectAuthenticationNotifications
                    {
                        SecurityTokenValidated = (context) =>
                        {
                            ClaimsIdentity identity = context.AuthenticationTicket.Identity;
                            foreach (Transformation current in identityProvider.Transformations)
                            {
                                current.Transform(identity, new TransformationContext(FederatedAuthenticationConfiguration, identityProvider));
                            }
                            return Task.FromResult(0);
                        }
                    }
                });
            }
        }
    }


  3. 새로운 만들어진 Provider를 MyProject.Owin.AzureADconfig 파일을 만들고, /Include 폴더에 적용시킨다.

      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    <?xml version="1.0" encoding="UTF-8"?>
    <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/">
        <sitecore role:require="Standalone or ContentDelivery or ContentManagement">
            <settings>
                <setting name="ClientId" value="{{Azure Web service의 클라이언트 아이디를 입력한다}}" />
                <setting name="AADInstance" value="https://login.microsoftonline.com/{0}" />
                <setting name="Tenant" value="{{Azure의 Tenant 정보를 입력한다.}}" />
                <setting name="PostLogoutRedirectURI" value="https://mysite/sitecore/login" />
                <setting name="RedirectURI" value="https://mysite/sitecore" />
            </settings>
            <pipelines>
                <owin.identityProviders help="Processors should derive from Sitecore.Owin.Authentication.Pipelines.IdentityProviders.IdentityProvidersProcessor">
                    <processor type="MyProject.Sitecore.Owin.AzureAD.Pipelines.CustomAzureADIdentityProvider, MyProject.Sitecore.Owin.AzureAD" resolve="true" />            </owin.identityProviders>
            </pipelines>
            <federatedAuthentication type="Sitecore.Owin.Authentication.Configuration.FederatedAuthenticationConfiguration, Sitecore.Owin.Authentication">
                <identityProvidersPerSites hint="list:AddIdentityProvidersPerSites">
                    <mapEntry name="0" type="Sitecore.Owin.Authentication.Collections.IdentityProvidersPerSitesMapEntry, Sitecore.Owin.Authentication">
                        <sites hint="list">
                            <site>shell</site>
                            <site>login</site>
                            <site>admin</site>
                            <site>service</site>
                            <site>modules_shell</site>
                            <site>modules_website</site>
                            <site>website</site>
                            <site>scheduler</site>
                            <site>system</site>
                            <site>publisher</site>
                        </sites>
                        <identityProviders hint="list:AddIdentityProvider">
                            <identityProvider ref="federatedAuthentication/identityProviders/identityProvider[@id='AzureAd']" />
                        </identityProviders>
                        <externalUserBuilder type="Sitecore.Owin.Authentication.Services.DefaultExternalUserBuilder, Sitecore.Owin.Authentication">
                            <param desc="isPersistentUser">true</param>
                        </externalUserBuilder>
                    </mapEntry>
                </identityProvidersPerSites>
                <identityProviders hint="list:AddIdentityProvider">
                    <identityProvider id="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>Sitecore Login by Microsoft ADFS</caption>
                        <icon>/sitecore/shell/themes/standard/Images/24x24/msazure.png</icon>
                        <domain>sitecore</domain>
                        <transformations hint="list:AddTransformation">
                            <transformation name="set idp claim" ref="federatedAuthentication/sharedTransformations/setIdpClaim" />
                            <!-- 해당 그룹의 모든 유저에게 "sitecore\developer" role의 권한을 준다. -->
                            <transformation name="Sitecore Client Users Role" type="Sitecore.Owin.Authentication.Services.DefaultTransformation, Sitecore.Owin.Authentication">
                                <sources hint="raw:AddSource">
                                    <claim name="groups" value="f5428848-ca62-4bcd-a3d1-189977c26c2e" />
                                </sources>
                                <targets hint="raw:AddTarget">
                                    <claim name="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" value="sitecore\Sitecore Minimal Page Editor" />
                                </targets>
                                <keepSource>true</keepSource>
                            </transformation>
                        </transformations>
                    </identityProvider>
                </identityProviders>
                <sharedTransformations hint="list:AddSharedClaimsTransformation">
                    <setIdpClaim name="setIdpClaim" type="Sitecore.Owin.Authentication.Services.SetIdpClaimTransform, Sitecore.Owin.Authentication" />
                </sharedTransformations>
                <propertyInitializer type="Sitecore.Owin.Authentication.Services.PropertyInitializer, Sitecore.Owin.Authentication">
                    <maps hint="list">
                        <!-- Mapping을 통하여 해당 그룹에 있는 모든 유저에게 Administrator 권한을 준다. -->
                        <map name="Sitecore Admin to Administartor" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication">
                            <data hint="raw:AddData">
                                <source name="idp" value="AzureAd" />
                                <source name="groups" value="3e677ddd-89a7-4fab-9f33-b25c5c35f7e6" />
                                <target name="IsAdministrator" value="true" />
                            </data>
                        </map>
                        <!-- Mapping을 통하여 유저 Profile의 Email 필드에 유저 이메일 정보를 업데이트 한다. -->
                        <map name="Emailaddress to email" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication">
                            <data hint="raw:AddData">
                                <!-- <source name="idp" value="AzureAd" /> -->
                                <source name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn" />
                                <target name="Email" />
                            </data>
                        </map>
                        <!-- Mapping을 통하여 유저 Profile의 FullName 필드에 유저 이름을 업데이트 한다. -->
                        <map name="Name claim" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication">
                            <data hint="raw:AddData">
                                <source name="name" />
                                <target name="FullName" />
                            </data>
                        </map>
                        <!-- Mapping을 통하여 유저 Profile의 Comment 필드에 추가적인 정보를 업데이트 한다. -->
                        <map name="Description claim" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication">
                            <data hint="raw:AddData">
                                <source name="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" />
                                <target name="Comment" />
                            </data>
                        </map>
                    </maps>
                </propertyInitializer>
            </federatedAuthentication>
        </sitecore>
    </configuration>
    

  4. Azure Active Directory -> App registrations 에서 Sitecore CMS의 Sign-on URL을 등록한다.
  5. 등록되어진 사이트코어 App에서 Settings -> Reply URL에서 "https://{yoursitecoresite}/sitecore" 로 등록한다.
  6. Settings -> Required permissions에서 Window Azure Active Directory API의 Delecated Permission을 설정한다.
    1. 체크 "Access the directory as the signed-in user"
    2. 체크 "Sign in and read user profile"
    3. Save
  7. Manifest를 업데이트한다.
    1. "groupMembershipClaims": "SecurityGroup"
    2. "oauth2AllowImplicatFlow": true
  8. Sitecore 9+ 버전을 사용하고 있다면, Web.config 파일에서 "owin:AutomaticAppStartup" 세팅을 True로 설정한다.
  9. AzureAD에 등록된 이메일로 사이트코어 CMS에 로그인한다.