11using DfE . ExternalApplications . Web . Authentication ;
22using GovUK . Dfe . CoreLibs . Security . Configurations ;
3+ using GovUK . Dfe . CoreLibs . Security . EntraSso ;
34using GovUK . Dfe . CoreLibs . Security . Interfaces ;
45using Microsoft . AspNetCore . Authentication ;
56using Microsoft . AspNetCore . Authentication . Cookies ;
@@ -16,13 +17,14 @@ namespace DfE.ExternalApplications.Web.Security;
1617/// Priority order:
1718/// 1. If X-Service-Email header present: Uses Internal Service Auth (header-based forwarder)
1819/// 2. If TestAuthentication.Enabled is true: Uses Test scheme for all
19- /// 3. If AllowToggle is true AND request is from Cypress : Uses Test scheme
20- /// 4. Otherwise: Uses OIDC (Cookies + OIDC challenge/sign-out)
20+ /// 3. If EntraSso.Enabled is true: Uses Entra SSO scheme
21+ /// 4. Otherwise: Uses DfE Sign-In OIDC (Cookies + OIDC challenge/sign-out)
2122/// </summary>
2223public class DynamicAuthenticationSchemeProvider (
2324 IOptions < AuthenticationOptions > options ,
2425 IHttpContextAccessor httpContextAccessor ,
2526 IOptions < TestAuthenticationOptions > testAuthOptions ,
27+ IOptions < EntraSsoOptions > entraSsoOptions ,
2628 IConfiguration configuration )
2729 : AuthenticationSchemeProvider ( options )
2830{
@@ -33,7 +35,6 @@ private bool IsTestAuthGloballyEnabled()
3335
3436 private bool ShouldUseTestAuth ( )
3537 {
36- // Always use test auth if globally enabled
3738 if ( IsTestAuthGloballyEnabled ( ) )
3839 {
3940 return true ;
@@ -42,88 +43,83 @@ private bool ShouldUseTestAuth()
4243 return false ;
4344 }
4445
46+ private bool IsEntraSsoEnabled ( )
47+ {
48+ return entraSsoOptions . Value . Enabled ;
49+ }
50+
4551 private bool IsInternalServiceRequest ( )
4652 {
4753 var httpContext = httpContextAccessor . HttpContext ;
4854 if ( httpContext == null ) return false ;
4955
50- // Only engage Internal Service Auth if X-Service-Email header is present
5156 return httpContext . Request . Headers . ContainsKey ( "x-service-email" ) ;
5257 }
5358
59+ private string GetDefaultIdpScheme ( )
60+ {
61+ return IsEntraSsoEnabled ( )
62+ ? EntraSsoDefaults . AuthenticationScheme
63+ : OpenIdConnectDefaults . AuthenticationScheme ;
64+ }
65+
5466 public override Task < AuthenticationScheme ? > GetDefaultAuthenticateSchemeAsync ( )
5567 {
56- // PRIORITY 1: Internal Service Authentication (check header first - fastest)
5768 if ( IsInternalServiceRequest ( ) )
5869 {
5970 return GetSchemeAsync ( InternalServiceAuthenticationHandler . SchemeName ) ;
6071 }
6172
62- // PRIORITY 2: Test Authentication (if enabled or Cypress)
6373 if ( ShouldUseTestAuth ( ) )
6474 {
6575 return GetSchemeAsync ( TestAuthenticationHandler . SchemeName ) ;
6676 }
6777
68- // PRIORITY 3: Default to OIDC (Cookies)
6978 return GetSchemeAsync ( CookieAuthenticationDefaults . AuthenticationScheme ) ;
7079 }
7180
7281 public override Task < AuthenticationScheme ? > GetDefaultChallengeSchemeAsync ( )
7382 {
74- // Internal Service requests don't challenge - they authenticate directly
7583 if ( IsInternalServiceRequest ( ) )
7684 {
7785 return GetSchemeAsync ( InternalServiceAuthenticationHandler . SchemeName ) ;
7886 }
7987
80- // Test Auth uses its own challenge
8188 if ( ShouldUseTestAuth ( ) )
8289 {
8390 return GetSchemeAsync ( TestAuthenticationHandler . SchemeName ) ;
8491 }
8592
86- // Regular users: OIDC challenge (login page)
87- return GetSchemeAsync ( OpenIdConnectDefaults . AuthenticationScheme ) ;
93+ return GetSchemeAsync ( GetDefaultIdpScheme ( ) ) ;
8894 }
8995
9096 public override Task < AuthenticationScheme ? > GetDefaultForbidSchemeAsync ( )
9197 {
92- // Don't call GetDefaultChallengeSchemeAsync here as it might trigger recursion
93- // Instead, inline the logic with the cached result
94-
95- // Internal Service requests
9698 if ( IsInternalServiceRequest ( ) )
9799 {
98100 return GetSchemeAsync ( InternalServiceAuthenticationHandler . SchemeName ) ;
99101 }
100102
101- // Test Auth
102103 if ( ShouldUseTestAuth ( ) )
103104 {
104105 return GetSchemeAsync ( TestAuthenticationHandler . SchemeName ) ;
105106 }
106107
107- // Regular users: OIDC
108- return GetSchemeAsync ( OpenIdConnectDefaults . AuthenticationScheme ) ;
108+ return GetSchemeAsync ( GetDefaultIdpScheme ( ) ) ;
109109 }
110110
111111 public override Task < AuthenticationScheme ? > GetDefaultSignInSchemeAsync ( )
112112 {
113- // Always use Cookies for sign-in
114113 return GetSchemeAsync ( CookieAuthenticationDefaults . AuthenticationScheme ) ;
115114 }
116115
117116 public override Task < AuthenticationScheme ? > GetDefaultSignOutSchemeAsync ( )
118117 {
119118 if ( ShouldUseTestAuth ( ) )
120119 {
121- // Test auth signs out cookies only
122120 return GetSchemeAsync ( CookieAuthenticationDefaults . AuthenticationScheme ) ;
123121 }
124- // OIDC sign-out triggers federated sign-out
125- return GetSchemeAsync ( OpenIdConnectDefaults . AuthenticationScheme ) ;
122+
123+ return GetSchemeAsync ( GetDefaultIdpScheme ( ) ) ;
126124 }
127125}
128-
129-
0 commit comments