@@ -1742,14 +1742,14 @@ describe('SAMLAuthenticationProvider', () => {
17421742 } ) ;
17431743
17441744 mockOptions . client . asInternalUser . transport . request . mockResolvedValue ( {
1745- access_token : 'some -token' ,
1746- refresh_token : 'some -refresh-token' ,
1745+ access_token : 'essu_dev_some -token' ,
1746+ refresh_token : 'essu_dev_some -refresh-token' ,
17471747 realm : ELASTIC_CLOUD_SSO_REALM_NAME ,
17481748 authentication : mockUser ,
17491749 } ) ;
17501750 mockOptions . uiam ?. getUserProfileGrant . mockReturnValue ( {
17511751 type : 'uiamAccessToken' ,
1752- accessToken : 'some -token' ,
1752+ accessToken : 'essu_dev_some -token' ,
17531753 sharedSecret : 'some-secret' ,
17541754 } ) ;
17551755 mockOptions . client . asScoped . mockReturnValue ( mockScopedClusterClient ) ;
@@ -1777,20 +1777,20 @@ describe('SAMLAuthenticationProvider', () => {
17771777 AuthenticationResult . redirectTo ( '/test-base-path/some-path#some-app' , {
17781778 userProfileGrant : {
17791779 type : 'uiamAccessToken' ,
1780- accessToken : 'some -token' ,
1780+ accessToken : 'essu_dev_some -token' ,
17811781 sharedSecret : 'some-secret' ,
17821782 } ,
17831783 state : {
1784- accessToken : 'some -token' ,
1785- refreshToken : 'some -refresh-token' ,
1784+ accessToken : 'essu_dev_some -token' ,
1785+ refreshToken : 'essu_dev_some -refresh-token' ,
17861786 realm : ELASTIC_CLOUD_SSO_REALM_NAME ,
17871787 } ,
17881788 user : mockUser ,
17891789 } )
17901790 ) ;
17911791
17921792 expect ( mockOptions . uiam ?. getUserProfileGrant ) . toHaveBeenCalledTimes ( 1 ) ;
1793- expect ( mockOptions . uiam ?. getUserProfileGrant ) . toHaveBeenCalledWith ( 'some -token' ) ;
1793+ expect ( mockOptions . uiam ?. getUserProfileGrant ) . toHaveBeenCalledWith ( 'essu_dev_some -token' ) ;
17941794 expect ( mockOptions . client . asInternalUser . transport . request ) . toHaveBeenCalledWith ( {
17951795 method : 'POST' ,
17961796 path : '/_security/saml/authenticate' ,
@@ -1807,8 +1807,8 @@ describe('SAMLAuthenticationProvider', () => {
18071807 it ( 'properly constructs authentication headers when UIAM is enabled.' , async ( ) => {
18081808 const request = httpServerMock . createKibanaRequest ( { headers : { } } ) ;
18091809 const state = {
1810- accessToken : 'some -valid-token' ,
1811- refreshToken : 'some -valid-refresh-token' ,
1810+ accessToken : 'essu_dev_some -valid-token' ,
1811+ refreshToken : 'essu_dev_some -valid-refresh-token' ,
18121812 realm : ELASTIC_CLOUD_SSO_REALM_NAME ,
18131813 } ;
18141814 const authorization = `Bearer ${ state . accessToken } ` ;
@@ -1841,8 +1841,8 @@ describe('SAMLAuthenticationProvider', () => {
18411841
18421842 it ( 'fails if token invalidation fails.' , async ( ) => {
18431843 const request = httpServerMock . createKibanaRequest ( ) ;
1844- const accessToken = 'x -saml-token' ;
1845- const refreshToken = 'x -saml-refresh-token' ;
1844+ const accessToken = 'essu_dev_x -saml-token' ;
1845+ const refreshToken = 'essu_dev_x -saml-refresh-token' ;
18461846
18471847 const failureReason = new errors . ResponseError (
18481848 securityMock . createApiResponse ( { statusCode : 500 , body : { } } )
@@ -1859,17 +1859,17 @@ describe('SAMLAuthenticationProvider', () => {
18591859
18601860 expect ( mockOptions . uiam ?. invalidateSessionTokens ) . toHaveBeenCalledTimes ( 1 ) ;
18611861 expect ( mockOptions . uiam ?. invalidateSessionTokens ) . toHaveBeenCalledWith (
1862- 'x -saml-token' ,
1863- 'x -saml-refresh-token'
1862+ 'essu_dev_x -saml-token' ,
1863+ 'essu_dev_x -saml-refresh-token'
18641864 ) ;
18651865
18661866 expect ( mockOptions . client . asInternalUser . transport . request ) . not . toHaveBeenCalled ( ) ;
18671867 } ) ;
18681868
18691869 it ( 'redirects to `loggedOut` URL.' , async ( ) => {
18701870 const request = httpServerMock . createKibanaRequest ( ) ;
1871- const accessToken = 'x -saml-token' ;
1872- const refreshToken = 'x -saml-refresh-token' ;
1871+ const accessToken = 'essu_dev_x -saml-token' ;
1872+ const refreshToken = 'essu_dev_x -saml-refresh-token' ;
18731873
18741874 await expect (
18751875 provider . logout ( request , {
@@ -1881,8 +1881,8 @@ describe('SAMLAuthenticationProvider', () => {
18811881
18821882 expect ( mockOptions . uiam ?. invalidateSessionTokens ) . toHaveBeenCalledTimes ( 1 ) ;
18831883 expect ( mockOptions . uiam ?. invalidateSessionTokens ) . toHaveBeenCalledWith (
1884- 'x -saml-token' ,
1885- 'x -saml-refresh-token'
1884+ 'essu_dev_x -saml-token' ,
1885+ 'essu_dev_x -saml-refresh-token'
18861886 ) ;
18871887
18881888 expect ( mockOptions . client . asInternalUser . transport . request ) . not . toHaveBeenCalled ( ) ;
@@ -1893,8 +1893,8 @@ describe('SAMLAuthenticationProvider', () => {
18931893 it ( 'succeeds if token from the state is expired, but has been successfully refreshed.' , async ( ) => {
18941894 const request = httpServerMock . createKibanaRequest ( ) ;
18951895 const state = {
1896- accessToken : 'expired -token' ,
1897- refreshToken : 'valid -refresh-token' ,
1896+ accessToken : 'essu_dev_expired -token' ,
1897+ refreshToken : 'essu_dev_valid -refresh-token' ,
18981898 realm : 'cloud-saml-kibana' ,
18991899 } ;
19001900
@@ -1903,27 +1903,27 @@ describe('SAMLAuthenticationProvider', () => {
19031903 ) ;
19041904
19051905 mockOptions . uiam ?. refreshSessionTokens . mockResolvedValue ( {
1906- accessToken : 'new -access-token' ,
1907- refreshToken : 'new -refresh-token' ,
1906+ accessToken : 'essu_dev_new -access-token' ,
1907+ refreshToken : 'essu_dev_new -refresh-token' ,
19081908 } ) ;
19091909
19101910 mockOptions . uiam ?. getUserProfileGrant . mockReturnValue ( {
1911- accessToken : 'new -access-token' ,
1911+ accessToken : 'essu_dev_new -access-token' ,
19121912 sharedSecret : 'some-secret' ,
19131913 type : 'uiamAccessToken' ,
19141914 } ) ;
19151915
19161916 await expect ( provider . authenticate ( request , state ) ) . resolves . toEqual (
19171917 AuthenticationResult . succeeded ( mockUser , {
1918- authHeaders : { authorization : 'Bearer new -access-token' } ,
1918+ authHeaders : { authorization : 'Bearer essu_dev_new -access-token' } ,
19191919 userProfileGrant : {
1920- accessToken : 'new -access-token' ,
1920+ accessToken : 'essu_dev_new -access-token' ,
19211921 sharedSecret : 'some-secret' ,
19221922 type : 'uiamAccessToken' ,
19231923 } ,
19241924 state : {
1925- accessToken : 'new -access-token' ,
1926- refreshToken : 'new -refresh-token' ,
1925+ accessToken : 'essu_dev_new -access-token' ,
1926+ refreshToken : 'essu_dev_new -refresh-token' ,
19271927 realm : 'cloud-saml-kibana' ,
19281928 } ,
19291929 } )
@@ -1933,13 +1933,14 @@ describe('SAMLAuthenticationProvider', () => {
19331933 expect ( mockOptions . uiam ?. refreshSessionTokens ) . toHaveBeenCalledWith ( state . refreshToken ) ;
19341934
19351935 expect ( request . headers ) . not . toHaveProperty ( 'authorization' ) ;
1936+ expect ( request . headers ) . not . toHaveProperty ( ES_CLIENT_AUTHENTICATION_HEADER ) ;
19361937 } ) ;
19371938
19381939 it ( 'fails if token from the state is expired, refresh attempt failed, and displays error from UIAM' , async ( ) => {
19391940 const request = httpServerMock . createKibanaRequest ( { headers : { } } ) ;
19401941 const state = {
1941- accessToken : 'expired -token' ,
1942- refreshToken : 'invalid -refresh-token' ,
1942+ accessToken : 'essu_dev_expired -token' ,
1943+ refreshToken : 'essu_dev_invalid -refresh-token' ,
19431944 realm : 'cloud-saml-kibana' ,
19441945 } ;
19451946 const authorization = `Bearer ${ state . accessToken } ` ;
@@ -1963,6 +1964,227 @@ describe('SAMLAuthenticationProvider', () => {
19631964 } ) ;
19641965
19651966 expect ( request . headers ) . not . toHaveProperty ( 'authorization' ) ;
1967+ expect ( request . headers ) . not . toHaveProperty ( ES_CLIENT_AUTHENTICATION_HEADER ) ;
1968+ } ) ;
1969+ } ) ;
1970+ } ) ;
1971+
1972+ describe ( 'UIAM mode with ES native tokens' , ( ) => {
1973+ beforeEach ( ( ) => {
1974+ mockUser = mockAuthenticatedUser ( {
1975+ authentication_provider : { type : 'saml' , name : ELASTIC_CLOUD_SSO_REALM_NAME } ,
1976+ } ) ;
1977+ mockOptions = mockAuthenticationProviderOptions ( {
1978+ name : ELASTIC_CLOUD_SSO_REALM_NAME ,
1979+ uiam : true ,
1980+ } ) ;
1981+
1982+ mockOptions . client . asInternalUser . transport . request . mockResolvedValue ( {
1983+ access_token : 'x_essu_dev_some-token' ,
1984+ refresh_token : 'x_essu_dev_some-refresh-token' ,
1985+ realm : ELASTIC_CLOUD_SSO_REALM_NAME ,
1986+ authentication : mockUser ,
1987+ } ) ;
1988+ mockOptions . client . asScoped . mockReturnValue ( mockScopedClusterClient ) ;
1989+
1990+ provider = new SAMLAuthenticationProvider ( mockOptions , {
1991+ realm : ELASTIC_CLOUD_SSO_REALM_NAME ,
1992+ } ) ;
1993+ } ) ;
1994+
1995+ describe ( '`login` method' , ( ) => {
1996+ it ( 'properly constructs ES native user profile activate grant when UIAM is enabled.' , async ( ) => {
1997+ const request = httpServerMock . createKibanaRequest ( ) ;
1998+ await expect (
1999+ provider . login (
2000+ request ,
2001+ { type : SAMLLogin . LoginWithSAMLResponse , samlResponse : mockSAMLSet1 . samlResponse } ,
2002+ {
2003+ requestIdMap : {
2004+ [ mockSAMLSet1 . requestId ] : { redirectURL : '/test-base-path/some-path#some-app' } ,
2005+ } ,
2006+ realm : ELASTIC_CLOUD_SSO_REALM_NAME ,
2007+ }
2008+ )
2009+ ) . resolves . toEqual (
2010+ AuthenticationResult . redirectTo ( '/test-base-path/some-path#some-app' , {
2011+ userProfileGrant : { type : 'accessToken' , accessToken : 'x_essu_dev_some-token' } ,
2012+ state : {
2013+ accessToken : 'x_essu_dev_some-token' ,
2014+ refreshToken : 'x_essu_dev_some-refresh-token' ,
2015+ realm : ELASTIC_CLOUD_SSO_REALM_NAME ,
2016+ } ,
2017+ user : mockUser ,
2018+ } )
2019+ ) ;
2020+
2021+ expect ( mockOptions . uiam ?. getUserProfileGrant ) . not . toHaveBeenCalled ( ) ;
2022+ expect ( mockOptions . client . asInternalUser . transport . request ) . toHaveBeenCalledWith ( {
2023+ method : 'POST' ,
2024+ path : '/_security/saml/authenticate' ,
2025+ body : {
2026+ ids : [ mockSAMLSet1 . requestId ] ,
2027+ content : mockSAMLSet1 . samlResponse ,
2028+ realm : ELASTIC_CLOUD_SSO_REALM_NAME ,
2029+ } ,
2030+ } ) ;
2031+ } ) ;
2032+ } ) ;
2033+
2034+ describe ( '`authenticate` method' , ( ) => {
2035+ it ( 'properly constructs authentication headers only with ES native access token when UIAM is enabled.' , async ( ) => {
2036+ const request = httpServerMock . createKibanaRequest ( { headers : { } } ) ;
2037+ const state = {
2038+ accessToken : 'x_essu_dev_some-valid-token' ,
2039+ refreshToken : 'x_essu_dev_some-valid-refresh-token' ,
2040+ realm : ELASTIC_CLOUD_SSO_REALM_NAME ,
2041+ } ;
2042+ const authorization = `Bearer ${ state . accessToken } ` ;
2043+
2044+ await expect ( provider . authenticate ( request , state ) ) . resolves . toEqual (
2045+ AuthenticationResult . succeeded ( mockUser , { authHeaders : { authorization } } )
2046+ ) ;
2047+
2048+ expect ( mockOptions . client . asScoped ) . toHaveBeenCalledWith ( { headers : { authorization } } ) ;
2049+
2050+ expect ( request . headers ) . not . toHaveProperty ( 'authorization' ) ;
2051+ expect ( request . headers ) . not . toHaveProperty ( ES_CLIENT_AUTHENTICATION_HEADER ) ;
2052+ } ) ;
2053+ } ) ;
2054+
2055+ describe ( '`logout` method' , ( ) => {
2056+ it ( 'returns `notHandled` if state is not presented or does not include access token.' , async ( ) => {
2057+ const request = httpServerMock . createKibanaRequest ( ) ;
2058+
2059+ await expect ( provider . logout ( request ) ) . resolves . toEqual (
2060+ DeauthenticationResult . notHandled ( )
2061+ ) ;
2062+
2063+ expect ( mockOptions . client . asInternalUser . transport . request ) . not . toHaveBeenCalled ( ) ;
2064+ } ) ;
2065+
2066+ it ( 'fails if token invalidation fails.' , async ( ) => {
2067+ const request = httpServerMock . createKibanaRequest ( ) ;
2068+ const accessToken = 'x_essu_dev_x-saml-token' ;
2069+ const refreshToken = 'x_essu_dev_x-saml-refresh-token' ;
2070+
2071+ const failureReason = new errors . ResponseError (
2072+ securityMock . createApiResponse ( { statusCode : 500 , body : { } } )
2073+ ) ;
2074+ mockOptions . client . asInternalUser . transport . request . mockRejectedValue ( failureReason ) ;
2075+
2076+ await expect (
2077+ provider . logout ( request , {
2078+ accessToken,
2079+ refreshToken,
2080+ realm : ELASTIC_CLOUD_SSO_REALM_NAME ,
2081+ } )
2082+ ) . resolves . toEqual ( DeauthenticationResult . failed ( failureReason ) ) ;
2083+
2084+ expect ( mockOptions . uiam ?. invalidateSessionTokens ) . not . toHaveBeenCalled ( ) ;
2085+
2086+ expect ( mockOptions . client . asInternalUser . transport . request ) . toHaveBeenCalledTimes ( 1 ) ;
2087+ expect ( mockOptions . client . asInternalUser . transport . request ) . toHaveBeenCalledWith ( {
2088+ method : 'POST' ,
2089+ path : '/_security/saml/logout' ,
2090+ body : { token : accessToken , refresh_token : refreshToken } ,
2091+ } ) ;
2092+ } ) ;
2093+
2094+ it ( 'redirects to `loggedOut` URL.' , async ( ) => {
2095+ const request = httpServerMock . createKibanaRequest ( ) ;
2096+ const accessToken = 'x_essu_dev_x-saml-token' ;
2097+ const refreshToken = 'x_essu_dev_x-saml-refresh-token' ;
2098+
2099+ mockOptions . client . asInternalUser . transport . request . mockResolvedValue ( { redirect : null } ) ;
2100+
2101+ await expect (
2102+ provider . logout ( request , {
2103+ accessToken,
2104+ refreshToken,
2105+ realm : 'test-realm' ,
2106+ } )
2107+ ) . resolves . toEqual ( DeauthenticationResult . redirectTo ( mockOptions . urls . loggedOut ( request ) ) ) ;
2108+
2109+ expect ( mockOptions . uiam ?. invalidateSessionTokens ) . not . toHaveBeenCalled ( ) ;
2110+
2111+ expect ( mockOptions . client . asInternalUser . transport . request ) . toHaveBeenCalledTimes ( 1 ) ;
2112+ expect ( mockOptions . client . asInternalUser . transport . request ) . toHaveBeenCalledWith ( {
2113+ method : 'POST' ,
2114+ path : '/_security/saml/logout' ,
2115+ body : { token : accessToken , refresh_token : refreshToken } ,
2116+ } ) ;
2117+ } ) ;
2118+ } ) ;
2119+
2120+ describe ( 'refresh token handling' , ( ) => {
2121+ it ( 'succeeds if token from the state is expired, but has been successfully refreshed.' , async ( ) => {
2122+ const request = httpServerMock . createKibanaRequest ( ) ;
2123+ const state = {
2124+ accessToken : 'x_essu_dev_expired-token' ,
2125+ refreshToken : 'x_essu_dev_valid-refresh-token' ,
2126+ realm : 'cloud-saml-kibana' ,
2127+ } ;
2128+
2129+ mockScopedClusterClient . asCurrentUser . security . authenticate . mockRejectedValueOnce (
2130+ new errors . ResponseError ( securityMock . createApiResponse ( { statusCode : 401 , body : { } } ) )
2131+ ) ;
2132+
2133+ mockOptions . tokens . refresh . mockResolvedValue ( {
2134+ accessToken : 'x_essu_dev_new-access-token' ,
2135+ refreshToken : 'x_essu_dev_new-refresh-token' ,
2136+ authenticationInfo : mockUser ,
2137+ } ) ;
2138+
2139+ await expect ( provider . authenticate ( request , state ) ) . resolves . toEqual (
2140+ AuthenticationResult . succeeded ( mockUser , {
2141+ authHeaders : { authorization : 'Bearer x_essu_dev_new-access-token' } ,
2142+ state : {
2143+ accessToken : 'x_essu_dev_new-access-token' ,
2144+ refreshToken : 'x_essu_dev_new-refresh-token' ,
2145+ realm : 'cloud-saml-kibana' ,
2146+ } ,
2147+ } )
2148+ ) ;
2149+
2150+ expect ( mockOptions . uiam ?. refreshSessionTokens ) . not . toHaveBeenCalled ( ) ;
2151+
2152+ expect ( mockOptions . tokens . refresh ) . toHaveBeenCalledTimes ( 1 ) ;
2153+ expect ( mockOptions . tokens . refresh ) . toHaveBeenCalledWith ( state . refreshToken ) ;
2154+
2155+ expect ( request . headers ) . not . toHaveProperty ( 'authorization' ) ;
2156+ expect ( request . headers ) . not . toHaveProperty ( ES_CLIENT_AUTHENTICATION_HEADER ) ;
2157+ } ) ;
2158+
2159+ it ( 'fails if token from the state is expired, refresh attempt failed, and displays error from UIAM' , async ( ) => {
2160+ const request = httpServerMock . createKibanaRequest ( { headers : { } } ) ;
2161+ const state = {
2162+ accessToken : 'x_essu_dev_expired-token' ,
2163+ refreshToken : 'x_essu_dev_invalid-refresh-token' ,
2164+ realm : 'cloud-saml-kibana' ,
2165+ } ;
2166+ const authorization = `Bearer ${ state . accessToken } ` ;
2167+
2168+ mockScopedClusterClient . asCurrentUser . security . authenticate . mockRejectedValue (
2169+ new errors . ResponseError ( securityMock . createApiResponse ( { statusCode : 401 , body : { } } ) )
2170+ ) ;
2171+
2172+ const refreshFailureReason = new Boom . Boom ( 'Authentication failed' ) ;
2173+ mockOptions . tokens . refresh . mockRejectedValue ( refreshFailureReason ) ;
2174+
2175+ await expect ( provider . authenticate ( request , state ) ) . resolves . toEqual (
2176+ AuthenticationResult . failed ( refreshFailureReason as any )
2177+ ) ;
2178+
2179+ expect ( mockOptions . uiam ?. refreshSessionTokens ) . not . toHaveBeenCalled ( ) ;
2180+
2181+ expect ( mockOptions . tokens . refresh ) . toHaveBeenCalledTimes ( 1 ) ;
2182+ expect ( mockOptions . tokens . refresh ) . toHaveBeenCalledWith ( state . refreshToken ) ;
2183+
2184+ expect ( mockOptions . client . asScoped ) . toHaveBeenCalledWith ( { headers : { authorization } } ) ;
2185+
2186+ expect ( request . headers ) . not . toHaveProperty ( 'authorization' ) ;
2187+ expect ( request . headers ) . not . toHaveProperty ( ES_CLIENT_AUTHENTICATION_HEADER ) ;
19662188 } ) ;
19672189 } ) ;
19682190 } ) ;
0 commit comments