@@ -26,6 +26,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2626using Microsoft . IdentityModel . Protocols . OpenIdConnect ;
2727using Microsoft . IdentityModel . Tokens ;
2828using System ;
29+ using System . Collections . Generic ;
2930using System . Configuration ;
3031using System . Globalization ;
3132using System . IdentityModel . Tokens . Jwt ;
@@ -71,16 +72,16 @@ internal class TokenValidationHandler : DelegatingHandler
7172 private string _authority ;
7273 private string _clientId ;
7374 private ConfigurationManager < OpenIdConnectConfiguration > _configManager ;
74-
75+ private string _tenant ;
7576 private ISecurityTokenValidator _tokenValidator ;
7677
7778 public TokenValidationHandler ( )
7879 {
7980 _audience = ConfigurationManager . AppSettings [ "ida:Audience" ] ;
8081 _clientId = ConfigurationManager . AppSettings [ "ida:ClientId" ] ;
8182 var aadInstance = ConfigurationManager . AppSettings [ "ida:AADInstance" ] ;
82- var tenant = ConfigurationManager . AppSettings [ "ida:Tenant " ] ;
83- _authority = string . Format ( CultureInfo . InvariantCulture , aadInstance , tenant ) ;
83+ _tenant = ConfigurationManager . AppSettings [ "ida:TenantId " ] ;
84+ _authority = string . Format ( CultureInfo . InvariantCulture , aadInstance , _tenant ) ;
8485 _configManager = new ConfigurationManager < OpenIdConnectConfiguration > ( $ "{ _authority } /.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever ( ) ) ;
8586 _tokenValidator = new JwtSecurityTokenHandler ( ) ;
8687 }
@@ -93,6 +94,9 @@ public TokenValidationHandler()
9394 /// <returns>A <see cref="HttpResponseMessage"/>.</returns>
9495 protected async override Task < HttpResponseMessage > SendAsync ( HttpRequestMessage request , CancellationToken cancellationToken )
9596 {
97+ // For debugging/development purposes, one can enable additional detail in exceptions by setting IdentityModelEventSource.ShowPII to true.
98+ Microsoft . IdentityModel . Logging . IdentityModelEventSource . ShowPII = true ;
99+
96100 // check if there is a jwt in the authorization header, return 'Unauthorized' error if the token is null.
97101 if ( request . Headers . Authorization == null || request . Headers . Authorization . Parameter == null )
98102 return BuildResponseErrorMessage ( HttpStatusCode . Unauthorized ) ;
@@ -104,19 +108,35 @@ protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage
104108 {
105109 config = await _configManager . GetConfigurationAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
106110 }
107- catch ( Exception )
111+ catch ( Exception ex )
108112 {
113+ #if DEBUG
114+ return BuildResponseErrorMessage ( HttpStatusCode . InternalServerError , ex . Message ) ;
115+ #else
109116 return new HttpResponseMessage ( HttpStatusCode . InternalServerError ) ;
117+ #endif
110118 }
111119
120+ // You can get a list of issuers for the various Azure AD deployments (global & sovereign) from the following endpoint
121+ //https://login.microsoftonline.com/common/discovery/instance?authorization_endpoint=https://login.microsoftonline.com/common/oauth2/v2.0/authorize&api-version=1.1;
122+
123+ IList < string > validissuers = new List < string > ( )
124+ {
125+ $ "https://login.microsoftonline.com/{ _tenant } /",
126+ $ "https://login.microsoftonline.com/{ _tenant } /v2.0",
127+ $ "https://login.windows.net/{ _tenant } /",
128+ $ "https://login.microsoft.com/{ _tenant } /",
129+ $ "https://sts.windows.net/{ _tenant } /"
130+ } ;
131+
112132 // Initialize the token validation parameters
113133 TokenValidationParameters validationParameters = new TokenValidationParameters
114134 {
115135 // App Id URI and AppId of this service application are both valid audiences.
116136 ValidAudiences = new [ ] { _audience , _clientId } ,
117137
118138 // Support Azure AD V1 and V2 endpoints.
119- ValidIssuers = new [ ] { config . Issuer , $ " { config . Issuer } /v2.0" } ,
139+ ValidIssuers = validissuers ,
120140 IssuerSigningKeys = config . SigningKeys
121141 } ;
122142
@@ -130,7 +150,11 @@ protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage
130150 if ( ! claimsPrincipal . Claims . Any ( x => x . Type == ClaimConstants . ScopeClaimType )
131151 && ! claimsPrincipal . Claims . Any ( y => y . Type == ClaimConstants . RolesClaimType ) )
132152 {
133- throw new UnauthorizedAccessException ( "Neither scope or roles claim was found in the bearer token." ) ;
153+ #if DEBUG
154+ return BuildResponseErrorMessage ( HttpStatusCode . Forbidden , "Neither 'scope' or 'roles' claim was found in the bearer token." ) ;
155+ #else
156+ return BuildResponseErrorMessage ( HttpStatusCode . Forbidden ) ;
157+ #endif
134158 }
135159#pragma warning restore 1998
136160
@@ -147,22 +171,30 @@ protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage
147171
148172 return await base . SendAsync ( request , cancellationToken ) ;
149173 }
150- catch ( SecurityTokenValidationException )
174+ catch ( SecurityTokenValidationException stex )
151175 {
176+ #if DEBUG
177+ return BuildResponseErrorMessage ( HttpStatusCode . Unauthorized , stex . Message ) ;
178+ #else
152179 return BuildResponseErrorMessage ( HttpStatusCode . Unauthorized ) ;
180+ #endif
153181 }
154- catch ( Exception )
182+ catch ( Exception ex )
155183 {
184+ #if DEBUG
185+ return BuildResponseErrorMessage ( HttpStatusCode . InternalServerError , ex . Message ) ;
186+ #else
156187 return new HttpResponseMessage ( HttpStatusCode . InternalServerError ) ;
188+ #endif
157189 }
158190 }
159191
160- private HttpResponseMessage BuildResponseErrorMessage ( HttpStatusCode statusCode )
192+ private HttpResponseMessage BuildResponseErrorMessage ( HttpStatusCode statusCode , string error_description = "" )
161193 {
162194 var response = new HttpResponseMessage ( statusCode ) ;
163195
164196 // The Scheme should be "Bearer", authorization_uri should point to the tenant url and resource_id should point to the audience.
165- var authenticateHeader = new AuthenticationHeaderValue ( "Bearer" , "authorization_uri=\" " + _authority + "\" " + "," + "resource_id=" + _audience ) ;
197+ var authenticateHeader = new AuthenticationHeaderValue ( "Bearer" , "authorization_uri=\" " + _authority + "\" " + "," + "resource_id=" + _audience + $ ",error_description= { error_description } " ) ;
166198 response . Headers . WwwAuthenticate . Add ( authenticateHeader ) ;
167199 return response ;
168200 }
0 commit comments