Skip to content

Commit 3f4a285

Browse files
authored
Merge pull request #4 from dark-loop/intgr-schemes
adding scheme integrations for attribute data
2 parents e8a4f45 + aa6141b commit 3f4a285

10 files changed

+60
-75
lines changed

sample/DarkLoop.Azure.Functions.Authorize.SampleFunctions/DarkLoop.Azure.Functions.Authorize.SampleFunctions.csproj

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
<UserSecretsId>51dc0b9d-8e74-45ec-aebc-1d3d6934faf5</UserSecretsId>
66
</PropertyGroup>
77
<ItemGroup>
8+
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.1" />
9+
<PackageReference Include="Microsoft.AspNetCore.Authorization.Policy" Version="2.2.0" />
810
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="3.1.0" />
911
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.12" />
1012
</ItemGroup>

sample/DarkLoop.Azure.Functions.Authorize.SampleFunctions/Startup.cs

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public override void Configure(IFunctionsHostBuilder builder)
4646
//var response = x.Response;
4747
//response.ContentType = "text/plain";
4848
//response.ContentLength = 5;
49+
//response.StatusCode = 401;
4950
//await response.WriteAsync("No go");
5051
//await response.Body.FlushAsync();
5152
}

src/DarkLoop.Azure.Functions.Authorize/Bindings/FunctionsAuthorizeBindingProvider.cs

+4-2
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,14 @@ public FunctionsAuthorizeBindingProvider(IFunctionsAuthorizationFilterIndex filt
2121
_filtersIndex = filterIndex;
2222
}
2323

24-
public async Task<IBinding> TryCreateAsync(BindingProviderContext context)
24+
public async Task<IBinding?> TryCreateAsync(BindingProviderContext context)
2525
{
2626
if (context == null) throw new ArgumentNullException(nameof(context));
2727

2828
var paramType = context.Parameter.ParameterType;
2929
if (paramType == typeof(HttpRequest) || paramType == typeof(HttpRequestMessage))
3030
{
3131
await this.ProcessAuthorizationAsync(context.Parameter);
32-
Console.WriteLine($"Processed auth info for {context.Parameter.Member.Name}.");
3332
}
3433

3534
return null;
@@ -38,6 +37,9 @@ public async Task<IBinding> TryCreateAsync(BindingProviderContext context)
3837
private Task ProcessAuthorizationAsync(ParameterInfo info)
3938
{
4039
var method = info.Member as MethodInfo;
40+
41+
if (method == null) throw new InvalidOperationException($"Unable to bind authorization context for {info.Name}.");
42+
4143
var cls = method.DeclaringType;
4244

4345
var allowAnonymous = method.GetCustomAttribute<AllowAnonymousAttribute>();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace DarkLoop.Azure.Functions.Authorize
6+
{
7+
internal class Constants
8+
{
9+
internal const string AuthInvokedKey = "__WebJobAuthInvoked";
10+
internal const string WebJobsAuthScheme = "WebJobsAuthLevel";
11+
}
12+
}

src/DarkLoop.Azure.Functions.Authorize/DarkLoop.Azure.Functions.Authorize.csproj

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
<Product>Azure Functions Authorize</Product>
88
<Description>Allows same AuthorizeAttribute behavior for Azure Functions V3+</Description>
99
<AzureFunctionsVersion>v3</AzureFunctionsVersion>
10+
<Nullable>enable</Nullable>
11+
<UserSecretsId>3472ff41-3859-4101-a2da-6c37d751fd31</UserSecretsId>
1012
</PropertyGroup>
1113

1214
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
@@ -21,10 +23,9 @@
2123

2224
<ItemGroup>
2325
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.2.0" />
24-
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="3.0.0" />
26+
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="3.0.3" />
2527
<PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.1.0" />
2628
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Http" Version="3.0.2" />
27-
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.10.0" />
2829
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.12">
2930
<IncludeAssets>
3031
<IsPackable>true</IsPackable>

src/DarkLoop.Azure.Functions.Authorize/Filters/FunctionsAuthorizeFilter.cs

+28-62
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ namespace DarkLoop.Azure.Functions.Authorize.Filters
1313
{
1414
internal class FunctionsAuthorizeFilter : IFunctionsAuthorizeFilter
1515
{
16-
private const string AuthInvokedKey = "__WebJobAuthInvoked";
17-
1816
public IEnumerable<IAuthorizeData> AuthorizeData { get; }
1917

2018
public IAuthenticationSchemeProvider SchemeProvider { get; }
@@ -23,99 +21,67 @@ internal class FunctionsAuthorizeFilter : IFunctionsAuthorizeFilter
2321

2422
public AuthorizationPolicy Policy { get; }
2523

26-
public FunctionsAuthorizeFilter(IEnumerable<IAuthorizeData> authorizeData)
27-
{
28-
this.AuthorizeData = authorizeData;
29-
}
30-
3124
public FunctionsAuthorizeFilter(
3225
IAuthenticationSchemeProvider schemeProvider,
3326
IAuthorizationPolicyProvider policyProvider,
3427
IEnumerable<IAuthorizeData> authorizeData)
35-
: this(authorizeData)
3628
{
3729
this.SchemeProvider = schemeProvider;
3830
this.PolicyProvider = policyProvider;
39-
}
40-
41-
public FunctionsAuthorizeFilter(string policy)
42-
#pragma warning disable CS0618 // Type or member is obsolete
43-
: this(new[] { new FunctionAuthorizeAttribute(policy) }) { }
44-
#pragma warning restore CS0618 // Type or member is obsolete
31+
this.AuthorizeData = authorizeData;
4532

46-
public async Task AuthorizeAsync(FunctionAuthorizationContext context)
33+
this.IntegrateSchemes();
34+
this.Policy = this.ComputePolicyAsync().GetAwaiter().GetResult();
35+
}
36+
37+
private void IntegrateSchemes()
4738
{
48-
if (context is null) throw new ArgumentNullException(nameof(context));
39+
var schemes = this.SchemeProvider.GetAllSchemesAsync().GetAwaiter().GetResult();
40+
var strSchemes = string.Join(',',
41+
from scheme in schemes
42+
where scheme.Name != Constants.WebJobsAuthScheme
43+
select scheme.Name);
4944

50-
if (context.HttpContext.Items.ContainsKey(AuthInvokedKey))
45+
foreach (var data in this.AuthorizeData)
5146
{
52-
return;
47+
// only setting auth schemes if they have not been specified already
48+
if (string.IsNullOrWhiteSpace(data.AuthenticationSchemes))
49+
{
50+
data.AuthenticationSchemes = strSchemes;
51+
}
5352
}
53+
}
5454

55-
var effectivePolicy = await this.ComputePolicyAsync();
55+
public async Task AuthorizeAsync(FunctionAuthorizationContext context)
56+
{
57+
if (context is null) throw new ArgumentNullException(nameof(context));
5658

57-
if (effectivePolicy is null)
59+
if (context.HttpContext.Items.ContainsKey(Constants.AuthInvokedKey))
5860
{
5961
return;
6062
}
6163

6264
var httpContext = context.HttpContext;
63-
await this.AuthenticateRequestAsync(context);
6465
var evaluator = httpContext.RequestServices.GetRequiredService<IPolicyEvaluator>();
65-
var authenticateResult = await evaluator.AuthenticateAsync(effectivePolicy, context.HttpContext);
66-
var authorizeResult = await evaluator.AuthorizeAsync(effectivePolicy, authenticateResult, context.HttpContext, context);
66+
var authenticateResult = await evaluator.AuthenticateAsync(this.Policy, httpContext);
67+
var authorizeResult = await evaluator.AuthorizeAsync(this.Policy, authenticateResult, httpContext, context);
6768

6869
if (authorizeResult.Challenged)
6970
{
70-
context.Result = new ChallengeResult(effectivePolicy.AuthenticationSchemes.ToArray());
71+
context.Result = new ChallengeResult(this.Policy.AuthenticationSchemes.ToArray());
7172
}
7273
else if (authorizeResult.Forbidden)
7374
{
74-
context.Result = new ForbidResult(effectivePolicy.AuthenticationSchemes.ToArray());
75+
context.Result = new ForbidResult(this.Policy.AuthenticationSchemes.ToArray());
7576
}
76-
77-
}
78-
79-
private async Task<AuthenticateResult> AuthenticateRequestAsync(FunctionAuthorizationContext context)
80-
{
81-
var httpContext = context.HttpContext;
82-
var handlers = httpContext.RequestServices.GetService<IAuthenticationHandlerProvider>();
83-
84-
foreach (var scheme in await this.SchemeProvider.GetRequestHandlerSchemesAsync())
77+
else if (!authorizeResult.Succeeded)
8578
{
86-
var handler = await handlers.GetHandlerAsync(httpContext, scheme.Name) as IAuthenticationRequestHandler;
87-
if (handler != null)
88-
{
89-
var result = await handler.AuthenticateAsync();
90-
if (result.Succeeded)
91-
{
92-
httpContext.User = result.Principal;
93-
return result;
94-
}
95-
}
79+
context.Result = new UnauthorizedResult();
9680
}
97-
98-
var defaultAuthenticate = await this.SchemeProvider.GetDefaultAuthenticateSchemeAsync();
99-
if (defaultAuthenticate != null)
100-
{
101-
var result = await httpContext.AuthenticateAsync(defaultAuthenticate.Name);
102-
if (result?.Principal != null)
103-
{
104-
httpContext.User = result.Principal;
105-
return result;
106-
}
107-
}
108-
109-
return AuthenticateResult.NoResult();
11081
}
11182

11283
private Task<AuthorizationPolicy> ComputePolicyAsync()
11384
{
114-
if (this.Policy != null)
115-
{
116-
return Task.FromResult(this.Policy);
117-
}
118-
11985
if (this.PolicyProvider == null)
12086
{
12187
throw new InvalidOperationException("Policy cannot be created.");

src/DarkLoop.Azure.Functions.Authorize/FunctionAuthorizeAttribute.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ public FunctionAuthorizeAttribute(string policy)
2222
this.Policy = policy;
2323
}
2424

25-
public string Policy { get; set; }
25+
public string? Policy { get; set; }
2626

27-
public string Roles { get; set; }
27+
public string? Roles { get; set; }
2828

29-
public string AuthenticationSchemes { get; set; }
29+
public string? AuthenticationSchemes { get; set; }
3030

3131
async Task IFunctionInvocationFilter.OnExecutingAsync(FunctionExecutingContext executingContext, CancellationToken cancellationToken)
3232
{
@@ -53,7 +53,7 @@ private bool IsProcessed(FunctionExecutingContext context)
5353
return (bool)value;
5454
}
5555

56-
private HttpContext GetHttpContext(FunctionExecutingContext context)
56+
private HttpContext? GetHttpContext(FunctionExecutingContext context)
5757
{
5858
var requestOrMessage = context.Arguments.Values.FirstOrDefault(x => x is HttpRequest || x is HttpRequestMessage);
5959

src/DarkLoop.Azure.Functions.Authorize/Handlers/AuthenticationRequestHandler.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace DarkLoop.Azure.Functions.Authorize.Handlers
1212
{
1313
public static class AuthenticationRequestHandler
1414
{
15-
public static async Task<IActionResult> HandleAuthenticateAsync(HttpContext context, string scheme)
15+
public static async Task<IActionResult?> HandleAuthenticateAsync(HttpContext context, string scheme)
1616
{
1717
var handler = await GetSchemeHandler(context, scheme);
1818
if(await handler.HandleRequestAsync())

src/DarkLoop.Azure.Functions.Authorize/Security/AuthenticationExtensions.cs

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Security.Claims;
33
using System.Text;
44
using System.Threading.Tasks;
5+
using DarkLoop.Azure.Functions.Authorize;
56
using Microsoft.AspNetCore.Authentication;
67
using Microsoft.AspNetCore.Authentication.JwtBearer;
78
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
@@ -26,14 +27,14 @@ public static AuthenticationBuilder AddAuthentication(this IFunctionsHostBuilder
2627
}
2728

2829
public static AuthenticationBuilder AddAuthentication(
29-
this IFunctionsHostBuilder builder, Action<AuthenticationOptions> configure)
30+
this IFunctionsHostBuilder builder, Action<AuthenticationOptions>? configure)
3031
{
3132
if (builder == null)
3233
{
3334
throw new ArgumentNullException(nameof(builder));
3435
}
3536

36-
if(configure!=null)
37+
if (configure != null)
3738
{
3839
builder.Services.AddSingleton<IConfigureOptions<AuthenticationOptions>>(provider =>
3940
new ConfigureOptions<AuthenticationOptions>(options =>
@@ -49,7 +50,7 @@ public static AuthenticationBuilder AddAuthentication(
4950

5051
private static AuthenticationBuilder AddScriptFunctionsJwtBearer(this AuthenticationBuilder builder)
5152
{
52-
return builder.AddJwtBearer("WebJobsAuthLevel", options =>
53+
return builder.AddJwtBearer(Constants.WebJobsAuthScheme, options =>
5354
{
5455
options.Events = new JwtBearerEvents
5556
{

src/DarkLoop.Azure.Functions.Authorize/Security/FunctionAuthorizationContext.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ public FunctionAuthorizationContext(HttpContext httpContext)
1515

1616
public HttpContext HttpContext { get; }
1717

18-
public IActionResult Result { get; internal set; }
18+
public IActionResult? Result { get; internal set; }
1919
}
2020
}

0 commit comments

Comments
 (0)