@@ -53,17 +53,11 @@ public static IServiceCollection AddProtectedWebApi(
5353 configuration . Bind ( configSectionName , options ) ;
5454
5555 // This is an Microsoft identity platform Web API
56- var authority = options . Authority . Trim ( ) . TrimEnd ( '/' ) ;
57- if ( ! authority . EndsWith ( "v2.0" ) )
58- authority += "/v2.0" ;
59- options . Authority = authority ;
56+ EnsureAuthorityIsV2_0 ( options ) ;
6057
61- // The valid audience could be given as Client Id or as Uri. If it does not start with 'api://', this variant is added to the list of valid audiences.
62- var validAudiences = new List < string > { options . Audience } ;
63- if ( ! options . Audience . StartsWith ( "api://" , StringComparison . OrdinalIgnoreCase ) )
64- validAudiences . Add ( $ "api://{ options . Audience } ") ;
65-
66- options . TokenValidationParameters . ValidAudiences = validAudiences ;
58+ // The valid audience could be given as Client Id or as Uri.
59+ // If it does not start with 'api://', this variant is added to the list of valid audiences.
60+ EnsureValidAudiencesContainsApiGuidIfGuidProvided ( options ) ;
6761
6862 // Instead of using the default validation (validating against a single tenant, as we do in line of business apps),
6963 // we inject our own multi-tenant validation logic (which even accepts both v1.0 and v2.0 tokens)
@@ -80,17 +74,17 @@ public static IServiceCollection AddProtectedWebApi(
8074 options . Events = new JwtBearerEvents ( ) ;
8175
8276 options . Events . OnTokenValidated = async context =>
83- {
84- // This check is required to ensure that the Web API only accepts tokens from tenants where it has been consented and provisioned.
85- if ( ! context . Principal . Claims . Any ( x => x . Type == ClaimConstants . Scope )
86- && ! context . Principal . Claims . Any ( y => y . Type == ClaimConstants . Scp )
87- && ! context . Principal . Claims . Any ( y => y . Type == ClaimConstants . Roles ) )
88- {
89- throw new UnauthorizedAccessException ( "Neither scope or roles claim was found in the bearer token." ) ;
90- }
91-
92- await Task . FromResult ( 0 ) ;
93- } ;
77+ {
78+ // This check is required to ensure that the Web API only accepts tokens from tenants where it has been consented and provisioned.
79+ if ( ! context . Principal . Claims . Any ( x => x . Type == ClaimConstants . Scope )
80+ && ! context . Principal . Claims . Any ( y => y . Type == ClaimConstants . Scp )
81+ && ! context . Principal . Claims . Any ( y => y . Type == ClaimConstants . Roles ) )
82+ {
83+ throw new UnauthorizedAccessException ( "Neither scope or roles claim was found in the bearer token." ) ;
84+ }
85+
86+ await Task . FromResult ( 0 ) ;
87+ } ;
9488
9589 if ( subscribeToJwtBearerMiddlewareDiagnosticsEvents )
9690 {
@@ -130,5 +124,37 @@ public static IServiceCollection AddProtectedApiCallsWebApis(
130124
131125 return services ;
132126 }
127+
128+ /// <summary>
129+ /// Ensures that the authority is a v2.0 authority
130+ /// </summary>
131+ /// <param name="options">Jwt bearer options read from the config file
132+ /// or set by the developper, for which we want to ensure the authority
133+ /// is a v2.0 authority</param>
134+ internal static void EnsureAuthorityIsV2_0 ( JwtBearerOptions options )
135+ {
136+ var authority = options . Authority . Trim ( ) . TrimEnd ( '/' ) ;
137+ if ( ! authority . EndsWith ( "v2.0" ) )
138+ authority += "/v2.0" ;
139+ options . Authority = authority ;
140+ }
141+
142+
143+ /// <summary>
144+ /// Ensure that if the audience is a GUID, api://{audience} is also added
145+ /// as a valid audience (this is the default App ID URL in the app registration
146+ /// portal)
147+ /// </summary>
148+ /// <param name="options">Jwt bearer options for which to ensure that
149+ /// api://GUID is a valid audience</param>
150+ internal static void EnsureValidAudiencesContainsApiGuidIfGuidProvided ( JwtBearerOptions options )
151+ {
152+ var validAudiences = new List < string > { options . Audience } ;
153+ if ( ! options . Audience . StartsWith ( "api://" , StringComparison . OrdinalIgnoreCase )
154+ && Guid . TryParse ( options . Audience , out _ ) )
155+ validAudiences . Add ( $ "api://{ options . Audience } ") ;
156+
157+ options . TokenValidationParameters . ValidAudiences = validAudiences ;
158+ }
133159 }
134160}
0 commit comments