Skip to content

Commit 85c1a75

Browse files
authored
Make RegisterValidAudience aware of JsonWebToken (#2421)
RegisterValidAudience needed the claims but only knew about JwtSecurityToken. This updates it to retrieve the claims from JwtSecurityToken or a JsonWebToken.
1 parent a8bf5aa commit 85c1a75

File tree

3 files changed

+95
-20
lines changed

3 files changed

+95
-20
lines changed

src/Microsoft.Identity.Web/Resource/RegisterValidAudience.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.IdentityModel.Tokens.Jwt;
77
using System.Linq;
88
using Microsoft.Extensions.Options;
9+
using Microsoft.IdentityModel.JsonWebTokens;
910
using Microsoft.IdentityModel.Tokens;
1011

1112
namespace Microsoft.Identity.Web.Resource
@@ -57,11 +58,12 @@ public void RegisterAudienceValidation(
5758
SecurityToken securityToken,
5859
TokenValidationParameters validationParameters)
5960
{
60-
JwtSecurityToken? token = securityToken as JwtSecurityToken;
61-
if (token == null)
61+
var claims = securityToken switch
6262
{
63-
throw new SecurityTokenValidationException(IDWebErrorMessage.TokenIsNotJwtToken);
64-
}
63+
JwtSecurityToken jwtSecurityToken => jwtSecurityToken.Claims,
64+
JsonWebToken jwtWebToken => jwtWebToken.Claims,
65+
_ => throw new SecurityTokenValidationException(IDWebErrorMessage.TokenIsNotJwtToken),
66+
};
6567

6668
validationParameters.AudienceValidator = null;
6769

@@ -70,13 +72,13 @@ public void RegisterAudienceValidation(
7072
validationParameters.ValidAudiences == null)
7173
{
7274
// handle v2.0 access token or Azure AD B2C tokens (even if v1.0)
73-
if (IsB2C || token.Claims.Any(c => c.Type == Constants.Version && c.Value == Constants.V2))
75+
if (IsB2C || claims.Any(c => c.Type == Constants.Version && c.Value == Constants.V2))
7476
{
7577
validationParameters.ValidAudience = $"{ClientId}";
7678
}
7779

7880
// handle v1.0 access token
79-
else if (token.Claims.Any(c => c.Type == Constants.Version && c.Value == Constants.V1))
81+
else if (claims.Any(c => c.Type == Constants.Version && c.Value == Constants.V1))
8082
{
8183
validationParameters.ValidAudience = $"api://{ClientId}";
8284
}

src/Microsoft.Identity.Web/WebApiExtensions/MicrosoftIdentityWebApiAuthenticationBuilder.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using Microsoft.Extensions.DependencyInjection;
1010
using Microsoft.Identity.Client;
1111
using Microsoft.Identity.Web.Internal;
12+
using Microsoft.IdentityModel.JsonWebTokens;
1213

1314
namespace Microsoft.Identity.Web
1415
{
@@ -99,7 +100,8 @@ internal static void CallsWebApiImplementation(
99100

100101
options.Events.OnTokenValidated = async context =>
101102
{
102-
context.HttpContext.StoreTokenUsedToCallWebAPI(context.SecurityToken as JwtSecurityToken);
103+
// Only pass through a token if it is of an expected type
104+
context.HttpContext.StoreTokenUsedToCallWebAPI(context.SecurityToken is JwtSecurityToken or JsonWebToken ? context.SecurityToken : null);
103105
await onTokenValidatedHandler(context).ConfigureAwait(false);
104106
};
105107
});

tests/Microsoft.Identity.Web.Test/Resource/RegisterValidAudienceTests.cs

Lines changed: 84 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4+
using System;
45
using System.Collections.Generic;
56
using System.IdentityModel.Tokens.Jwt;
67
using System.Linq;
78
using System.Security.Claims;
89
using Microsoft.Identity.Web.Resource;
910
using Microsoft.Identity.Web.Test.Common;
11+
using Microsoft.IdentityModel.JsonWebTokens;
1012
using Microsoft.IdentityModel.Tokens;
1113
using Xunit;
1214

@@ -20,7 +22,7 @@ public class RegisterValidAudienceTests
2022
private const string V2 = "2.0";
2123
private const string V3 = "3.0";
2224
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
23-
private JwtSecurityToken _token;
25+
private SecurityToken _token;
2426
private RegisterValidAudience _registerValidAudience;
2527
private TokenValidationParameters _validationParams;
2628
private IEnumerable<string> _validAudiences;
@@ -36,7 +38,19 @@ public void ValidateAudience_FromToken(
3638
bool isB2C,
3739
string tokenVersion)
3840
{
39-
InitializeTests(isB2C, tokenVersion);
41+
InitializeTests(isB2C, tokenVersion, claims => new JwtSecurityToken(null, null, claims));
42+
AssertAudienceFromToken();
43+
}
44+
45+
[Theory]
46+
[InlineData(false, V1)]
47+
[InlineData(false, V2)]
48+
[InlineData(true, V1)]
49+
public void ValidateAudience_FromToken_JsonWeb(
50+
bool isB2C,
51+
string tokenVersion)
52+
{
53+
InitializeTests(isB2C, tokenVersion, claims => new TestJsonWebToken(claims));
4054
AssertAudienceFromToken();
4155
}
4256

@@ -48,7 +62,19 @@ public void ValidateAudience_ProvidedInValidAudience(
4862
bool isB2C,
4963
string tokenVersion)
5064
{
51-
InitializeTests(isB2C, tokenVersion);
65+
InitializeTests(isB2C, tokenVersion, claims => new JwtSecurityToken(null, null, claims));
66+
AssertAudienceProvidedInValidAudience();
67+
}
68+
69+
[Theory]
70+
[InlineData(false, V1)]
71+
[InlineData(false, V2)]
72+
[InlineData(true, V1)]
73+
public void ValidateAudience_ProvidedInValidAudience_JsonWeb(
74+
bool isB2C,
75+
string tokenVersion)
76+
{
77+
InitializeTests(isB2C, tokenVersion, claims => new TestJsonWebToken(claims));
5278
AssertAudienceProvidedInValidAudience();
5379
}
5480

@@ -60,7 +86,19 @@ public void ValidateAudience_ProvidedInValidAudiences(
6086
bool isB2C,
6187
string tokenVersion)
6288
{
63-
InitializeTests(isB2C, tokenVersion);
89+
InitializeTests(isB2C, tokenVersion, claims => new JwtSecurityToken(null, null, claims));
90+
AssertAudienceProvidedInValidAudiences();
91+
}
92+
93+
[Theory]
94+
[InlineData(false, V1)]
95+
[InlineData(false, V2)]
96+
[InlineData(true, V1)]
97+
public void ValidateAudience_ProvidedInValidAudiences_JsonWeb(
98+
bool isB2C,
99+
string tokenVersion)
100+
{
101+
InitializeTests(isB2C, tokenVersion, claims => new TestJsonWebToken(claims));
64102
AssertAudienceProvidedInValidAudiences();
65103
}
66104

@@ -70,13 +108,24 @@ public void InvalidAudience_AssertFails(
70108
bool isB2C,
71109
string tokenVersion)
72110
{
73-
InitializeTests(isB2C, tokenVersion);
111+
InitializeTests(isB2C, tokenVersion, claims => new JwtSecurityToken(null, null, claims));
112+
AssertFailureOnInvalidAudienceInToken();
113+
}
114+
115+
[Theory]
116+
[InlineData(false, V3)]
117+
public void InvalidAudience_AssertFails_JsonWeb(
118+
bool isB2C,
119+
string tokenVersion)
120+
{
121+
InitializeTests(isB2C, tokenVersion, claims => new TestJsonWebToken(claims));
74122
AssertFailureOnInvalidAudienceInToken();
75123
}
76124

77125
private void InitializeTests(
78126
bool isB2C,
79-
string tokenVersion)
127+
string tokenVersion,
128+
Func<IEnumerable<Claim>, SecurityToken> tokenGenerator)
80129
{
81130
_options = new MicrosoftIdentityOptions
82131
{
@@ -103,7 +152,7 @@ private void InitializeTests(
103152
new Claim(Audience, _expectedAudience),
104153
};
105154

106-
_token = new JwtSecurityToken(null, null, claims);
155+
_token = tokenGenerator(claims);
107156
_validationParams = new TokenValidationParameters();
108157
_registerValidAudience = new RegisterValidAudience();
109158
_registerValidAudience.RegisterAudienceValidation(_validationParams, _options);
@@ -116,8 +165,8 @@ private void AssertAudienceFromToken()
116165
_validAudiences,
117166
_token,
118167
_validationParams));
119-
Assert.Equal(_expectedAudience, _token.Audiences.FirstOrDefault());
120-
Assert.Single(_token.Audiences);
168+
Assert.Equal(_expectedAudience, Audiences.FirstOrDefault());
169+
Assert.Single(Audiences);
121170
}
122171

123172
private void AssertAudienceProvidedInValidAudience()
@@ -127,8 +176,8 @@ private void AssertAudienceProvidedInValidAudience()
127176
_validAudiences,
128177
_token,
129178
_validationParams));
130-
Assert.Equal(_expectedAudience, _token.Audiences.FirstOrDefault());
131-
Assert.Single(_token.Audiences);
179+
Assert.Equal(_expectedAudience, Audiences.FirstOrDefault());
180+
Assert.Single(Audiences);
132181
}
133182

134183
private void AssertAudienceProvidedInValidAudiences()
@@ -144,16 +193,38 @@ private void AssertAudienceProvidedInValidAudiences()
144193
_validAudiences,
145194
_token,
146195
_validationParams));
147-
Assert.Equal(_expectedAudience, _token.Audiences.FirstOrDefault());
148-
Assert.Single(_token.Audiences);
196+
Assert.Equal(_expectedAudience, Audiences.FirstOrDefault());
197+
Assert.Single(Audiences);
149198
}
150199

200+
private IEnumerable<string> Audiences => _token switch
201+
{
202+
JwtSecurityToken s => s.Audiences,
203+
TestJsonWebToken w => w.Audiences,
204+
_ => throw new System.NotImplementedException(),
205+
};
206+
151207
private void AssertFailureOnInvalidAudienceInToken()
152208
{
153209
Assert.Throws<SecurityTokenInvalidAudienceException>(() => _registerValidAudience.ValidateAudience(
154210
_validAudiences,
155211
_token,
156212
_validationParams));
157213
}
214+
215+
private class TestJsonWebToken : JsonWebToken
216+
{
217+
private const string TestJwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
218+
219+
public TestJsonWebToken(IEnumerable<Claim> claims)
220+
: base(TestJwt)
221+
{
222+
Claims = claims;
223+
}
224+
225+
public override IEnumerable<Claim> Claims { get; }
226+
227+
public new IEnumerable<string> Audiences => Claims.Where(c => c.Type == Audience).Select(c => c.Value);
228+
}
158229
}
159230
}

0 commit comments

Comments
 (0)