Skip to content

Commit dee7fae

Browse files
authored
Adding runtime site name to valid JWT audiences (slot scenarios) (#10187)
1 parent f138f38 commit dee7fae

File tree

3 files changed

+76
-18
lines changed

3 files changed

+76
-18
lines changed

src/WebJobs.Script.WebHost/Filters/JwtAuthenticationAttribute.cs

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -46,24 +46,7 @@ public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationTok
4646
var signingKeys = SecretsUtility.GetTokenIssuerSigningKeys();
4747
if (signingKeys.Length > 0)
4848
{
49-
var validationParameters = new TokenValidationParameters()
50-
{
51-
IssuerSigningKeys = signingKeys,
52-
AudienceValidator = AudienceValidator,
53-
IssuerValidator = IssuerValidator,
54-
ValidAudiences = new string[]
55-
{
56-
string.Format(SiteAzureFunctionsUriFormat, ScriptSettingsManager.Instance.GetSetting(AzureWebsiteName)),
57-
string.Format(SiteUriFormat, ScriptSettingsManager.Instance.GetSetting(AzureWebsiteName))
58-
},
59-
ValidIssuers = new string[]
60-
{
61-
AppServiceCoreUri,
62-
string.Format(ScmSiteUriFormat, ScriptSettingsManager.Instance.GetSetting(AzureWebsiteName)),
63-
string.Format(SiteUriFormat, ScriptSettingsManager.Instance.GetSetting(AzureWebsiteName))
64-
}
65-
};
66-
49+
var validationParameters = CreateTokenValidationParameters(signingKeys);
6750
if (JwtGenerator.IsTokenValid(token, validationParameters))
6851
{
6952
context.Request.SetAuthorizationLevel(AuthorizationLevel.Admin);
@@ -76,6 +59,40 @@ public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationTok
7659

7760
public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken) => Task.CompletedTask;
7861

62+
internal static TokenValidationParameters CreateTokenValidationParameters(SymmetricSecurityKey[] signingKeys)
63+
{
64+
string siteName = ScriptSettingsManager.Instance.GetSetting(AzureWebsiteName);
65+
string runtimeSiteName = ScriptSettingsManager.Instance.GetSetting(AzureWebsiteRuntimeSiteName);
66+
var audiences = new List<string>
67+
{
68+
string.Format(SiteAzureFunctionsUriFormat, siteName),
69+
string.Format(SiteUriFormat, siteName)
70+
};
71+
72+
if (!string.IsNullOrEmpty(runtimeSiteName) && !string.Equals(siteName, runtimeSiteName, StringComparison.OrdinalIgnoreCase))
73+
{
74+
// on a non-production slot, the runtime site name will differ from the site name
75+
// we allow both for audience
76+
audiences.Add(string.Format(SiteUriFormat, runtimeSiteName));
77+
}
78+
79+
var validationParameters = new TokenValidationParameters()
80+
{
81+
IssuerSigningKeys = signingKeys,
82+
AudienceValidator = AudienceValidator,
83+
IssuerValidator = IssuerValidator,
84+
ValidAudiences = audiences,
85+
ValidIssuers = new string[]
86+
{
87+
AppServiceCoreUri,
88+
string.Format(ScmSiteUriFormat, siteName),
89+
string.Format(SiteUriFormat, siteName)
90+
}
91+
};
92+
93+
return validationParameters;
94+
}
95+
7996
private static string IssuerValidator(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters)
8097
{
8198
if (!validationParameters.ValidIssuers.Any(p => string.Equals(issuer, p, StringComparison.OrdinalIgnoreCase)))

src/WebJobs.Script/EnvironmentSettingNames.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public static class EnvironmentSettingNames
3131
public const string CoreToolsEnvironment = "FUNCTIONS_CORETOOLS_ENVIRONMENT";
3232
public const string AzureWebsiteArmCacheEnabled = "WEBSITE_FUNCTIONS_ARMCACHE_ENABLED";
3333
public const string TestDataCapEnabled = "WEBSITE_FUNCTIONS_TESTDATA_CAP_ENABLED";
34+
public const string AzureWebsiteRuntimeSiteName = "WEBSITE_DEPLOYMENT_ID";
3435

3536
/// <summary>
3637
/// Environment variable dynamically set by the platform when it is safe to

test/WebJobs.Script.Tests/Filters/JwtAuthenticationAttributeTests.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System;
55
using System.Collections.Generic;
66
using System.IdentityModel.Tokens.Jwt;
7+
using System.Linq;
78
using System.Net;
89
using System.Net.Http;
910
using System.Net.Http.Headers;
@@ -14,6 +15,7 @@
1415
using System.Web.Http.Filters;
1516
using Microsoft.Azure.Web.DataProtection;
1617
using Microsoft.Azure.WebJobs.Extensions.Http;
18+
using Microsoft.Azure.WebJobs.Script.Config;
1719
using Microsoft.Azure.WebJobs.Script.WebHost;
1820
using Microsoft.Azure.WebJobs.Script.WebHost.Filters;
1921
using Microsoft.Azure.WebJobs.Script.WebHost.Security;
@@ -109,6 +111,44 @@ public async Task AuthenticateAsync_WithValidToken_Base64Encoding_SetsAdminAutho
109111
await AuthenticateAsync(token, ScriptConstants.SiteTokenHeaderName, AuthorizationLevel.Admin);
110112
}
111113

114+
[Theory]
115+
[InlineData("testsite", "testsite")]
116+
[InlineData("testsite", "testsite__5bb5")]
117+
[InlineData("testsite", null)]
118+
[InlineData("testsite", "")]
119+
public void CreateTokenValidationParameters_NonProductionSlot_HasExpectedAudiences(string siteName, string runtimeSiteName)
120+
{
121+
string azFuncAudience = string.Format(ScriptConstants.SiteAzureFunctionsUriFormat, siteName);
122+
string siteAudience = string.Format(ScriptConstants.SiteUriFormat, siteName);
123+
string runtimeSiteAudience = string.Format(ScriptConstants.SiteUriFormat, runtimeSiteName);
124+
125+
var testEnv = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
126+
{
127+
{ EnvironmentSettingNames.AzureWebsiteName, siteName },
128+
{ EnvironmentSettingNames.AzureWebsiteRuntimeSiteName, runtimeSiteName }
129+
};
130+
131+
using (new TestScopedSettings(ScriptSettingsManager.Instance, testEnv))
132+
{
133+
var signingKeys = SecretsUtility.GetTokenIssuerSigningKeys();
134+
var tokenValidationParameters = JwtAuthenticationAttribute.CreateTokenValidationParameters(signingKeys);
135+
var audiences = tokenValidationParameters.ValidAudiences.ToArray();
136+
137+
Assert.Equal(audiences[0], azFuncAudience);
138+
Assert.Equal(audiences[1], siteAudience);
139+
140+
if (string.Compare(siteName, runtimeSiteName, StringComparison.OrdinalIgnoreCase) == 0)
141+
{
142+
Assert.Equal(2, audiences.Length);
143+
}
144+
else if (!string.IsNullOrEmpty(runtimeSiteName))
145+
{
146+
Assert.Equal(3, audiences.Length);
147+
Assert.Equal(audiences[2], runtimeSiteAudience);
148+
}
149+
}
150+
}
151+
112152
public async Task AuthenticateAsync(string token, string headerName, AuthorizationLevel expectedLevel)
113153
{
114154
var controllerContext = new HttpControllerContext()

0 commit comments

Comments
 (0)