Skip to content

Commit 32f6615

Browse files
authored
Isolated (#21)
added isolated worker support removed nuspec file and use csproj. Isolated & Inprocess nuget changed release workflow add documentation
1 parent ae0d9ca commit 32f6615

File tree

54 files changed

+1966
-158
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+1966
-158
lines changed

.github/workflows/release.yml

+16-2
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,24 @@ jobs:
1818
- name: Create NuGet Package
1919
run: dotnet pack -c Release /p:Version=${{ github.event.release.tag_name }} /p:PackageReleaseNotes="See https://github.com/fmichellonet/AzureFunctions.Extensions.OpenIDConnect/releases/tag/${{ github.event.release.tag_name }}"
2020
working-directory: .\src
21-
- name: Archive NuGet Package
21+
- name: Archive OpenIDConnect NuGet Package
2222
uses: actions/upload-artifact@v1
2323
with:
2424
name: AzureFunctions.Extensions.OpenIDConnect.${{ github.event.release.tag_name }}.nupkg
2525
path: .\src\AzureFunctions.Extensions.OpenIDConnect\bin\Release\AzureFunctions.Extensions.OpenIDConnect.${{ github.event.release.tag_name }}.nupkg
26-
- name: Publish to Nuget.org
26+
- name: Publish OpenIDConnect to Nuget.org
2727
run: dotnet nuget push .\src\AzureFunctions.Extensions.OpenIDConnect\bin\Release\AzureFunctions.Extensions.OpenIDConnect.${{ github.event.release.tag_name }}.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json
28+
- name: Archive OpenIDConnect.InProcess NuGet Package
29+
uses: actions/upload-artifact@v1
30+
with:
31+
name: AzureFunctions.Extensions.OpenIDConnect.InProcess.${{ github.event.release.tag_name }}.nupkg
32+
path: .\src\AzureFunctions.Extensions.OpenIDConnect.InProcess\bin\Release\AzureFunctions.Extensions.OpenIDConnect.InProcess.${{ github.event.release.tag_name }}.nupkg
33+
- name: Publish OpenIDConnect.InProcess to Nuget.org
34+
run: dotnet nuget push .\src\AzureFunctions.Extensions.OpenIDConnect.InProcess\bin\Release\AzureFunctions.Extensions.OpenIDConnect.InProcess.${{ github.event.release.tag_name }}.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json
35+
- name: Archive OpenIDConnect.Isolated NuGet Package
36+
uses: actions/upload-artifact@v1
37+
with:
38+
name: AzureFunctions.Extensions.OpenIDConnect.Isolated.${{ github.event.release.tag_name }}.nupkg
39+
path: .\src\AzureFunctions.Extensions.OpenIDConnect.Isolated\bin\Release\AzureFunctions.Extensions.OpenIDConnect.Isolated.${{ github.event.release.tag_name }}.nupkg
40+
- name: Publish OpenIDConnect.Isolated to Nuget.org
41+
run: dotnet nuget push .\src\AzureFunctions.Extensions.OpenIDConnect.Isolated\bin\Release\AzureFunctions.Extensions.OpenIDConnect.Isolated.${{ github.event.release.tag_name }}.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json

README.md

+45-3
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,14 @@ Azure Functions using v3 or V4 runtime and of course an identity provider (e.g.
1515

1616
## How to use it
1717

18-
Add AzureFunctions.Extensions.OpenIDConnect NuGet package to your Azure Functions project.
18+
AzureFunctions.Extensions.OpenIDConnect support [In-Process and Isolated](https://learn.microsoft.com/en-us/azure/azure-functions/dotnet-isolated-process-guide#differences-with-net-class-library-functions) azure functions hosting
19+
and brings the same features set. Be sure to use the nuget package according to your hosting choice.
1920

20-
> dotnet package install AzureFunctions.Extensions.OpenIDConnect
21+
### In process
22+
23+
Add AzureFunctions.Extensions.OpenIDConnect.InProcess NuGet package to your Azure Functions project.
24+
25+
> dotnet package install AzureFunctions.Extensions.OpenIDConnect.InProcess
2126
2227
Now head over the Configure method of the Startup class, add configure OpenID-Connect the way you like it.
2328

@@ -41,7 +46,44 @@ namespace MySecuredApp
4146
}
4247
```
4348

44-
### Securing an Azure Function
49+
### Isolated
50+
51+
Add AzureFunctions.Extensions.OpenIDConnect.Isolated NuGet package to your Azure Functions project.
52+
53+
> dotnet package install AzureFunctions.Extensions.OpenIDConnect.Isolated
54+
55+
Now head over your entrypoint class, add configure the host like this :
56+
57+
```csharp
58+
static class Program
59+
{
60+
private static async Task Main(string[] args)
61+
{
62+
var host = new HostBuilder()
63+
.ConfigureFunctionsWorkerDefaults((context, builder) =>
64+
{
65+
builder.UseAuthorization();
66+
})
67+
.ConfigureServices((context, services) =>
68+
{
69+
services.AddOpenIDConnect(config =>
70+
{
71+
var audience = Environment.GetEnvironmentVariable("OpenIdConnect_Audience");
72+
var issuer = Environment.GetEnvironmentVariable("OpenIdConnect_Issuer");
73+
var issuerUrl = Environment.GetEnvironmentVariable("OpenIdConnect_IssuerUrl");
74+
75+
config.SetTokenValidation(TokenValidationParametersHelpers.Default(audience, issuer));
76+
config.SetIssuerBaseUrlConfiguration(issuerUrl);
77+
});
78+
})
79+
.Build();
80+
81+
await host.RunAsync();
82+
}
83+
}
84+
```
85+
86+
## Securing an Azure Function
4587
Now that everything is configured, you can decorate your http-triggered functions with the well known [Authorize](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authorization.authorizeattribute?view=aspnetcore-3.1) attribute as follows:
4688

4789
```csharp

src/AzureFunctions.Extensions.OpenIDConnect.Tests/AuthorizationServiceShould.cs renamed to src/AzureFunctions.Extensions.OpenIDConnect.InProcess.Tests/AuthorizationServiceShould.cs

+9-9
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,21 @@
22
using System.Collections.Generic;
33
using System.Security.Claims;
44
using System.Threading.Tasks;
5-
using AzureFunctions.Extensions.OpenIDConnect.Configuration;
5+
using AzureFunctions.Extensions.OpenIDConnect.InProcess.Configuration;
6+
using AzureFunctions.Extensions.OpenIDConnect.Tests;
67
using FluentAssertions;
78
using Microsoft.AspNetCore.Authorization;
89
using Microsoft.AspNetCore.Authorization.Infrastructure;
9-
using Microsoft.Azure.WebJobs.Host;
1010
using Microsoft.Extensions.DependencyInjection;
1111
using NSubstitute;
1212
using NUnit.Framework;
1313

14-
namespace AzureFunctions.Extensions.OpenIDConnect.Tests
14+
namespace AzureFunctions.Extensions.OpenIDConnect.InProcess.Tests
1515
{
1616
[TestFixture]
1717
public class AuthorizationServiceShould
1818
{
19-
19+
2020
[TestCase("locale", "fr", "fr", true)]
2121
[TestCase("locale", "it", "fr", false)]
2222
public async Task ApplyPolicyRequirements(string claimType, string claimValue, string claimRequiredValue, bool isAuthorized)
@@ -33,18 +33,18 @@ public async Task ApplyPolicyRequirements(string claimType, string claimValue, s
3333
builder.SetIssuerBaseUrlConfiguration("https://issuer.com/");
3434
builder.SetTokenValidation("issuer", "audience");
3535
builder.SetTypeCrawler(() => new Type[] { });
36-
builder.AddPolicy(policyName, policy => policy.Requirements.Add(new ClaimsAuthorizationRequirement(claimType, new []{ claimRequiredValue }) ));
36+
builder.AddPolicy(policyName, policy => policy.Requirements.Add(new ClaimsAuthorizationRequirement(claimType, new[] { claimRequiredValue })));
3737
});
38-
38+
3939
var provider = collection.BuildServiceProvider();
4040

4141
var retriever = provider.GetService<IAuthorizationRequirementsRetriever>();
4242
var authorizationService = provider.GetService<IAuthorizationService>();
4343

4444
var user = Substitute.For<ClaimsPrincipal>();
45-
user.Claims.Returns(new List<Claim> {localeClaim});
46-
47-
45+
user.Claims.Returns(new List<Claim> { localeClaim });
46+
47+
4848
// Act
4949
var requirements = retriever.ForAttribute(new AuthorizeAttribute(policyName));
5050
var authResult = await authorizationService.AuthorizeAsync(user, null, requirements);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFramework>net6.0</TargetFramework>
4+
</PropertyGroup>
5+
6+
<ItemGroup>
7+
<PackageReference Include="FluentAssertions" Version="6.7.0" />
8+
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
9+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
10+
<PackageReference Include="NSubstitute" Version="4.4.0" />
11+
<PackageReference Include="NUnit" Version="3.13.3" />
12+
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
13+
</ItemGroup>
14+
15+
<ItemGroup>
16+
<ProjectReference Include="..\AzureFunctions.Extensions.OpenIDConnect.InProcess\AzureFunctions.Extensions.OpenIDConnect.InProcess.csproj" />
17+
<ProjectReference Include="..\AzureFunctions.Extensions.OpenIDConnect.Tests\AzureFunctions.Extensions.OpenIDConnect.Tests.csproj" />
18+
</ItemGroup>
19+
20+
</Project>

src/AzureFunctions.Extensions.OpenIDConnect.Tests/DependencyInjectionShould.cs renamed to src/AzureFunctions.Extensions.OpenIDConnect.InProcess.Tests/DependencyInjectionShould.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
using System;
2-
using AzureFunctions.Extensions.OpenIDConnect.Configuration;
2+
using AzureFunctions.Extensions.OpenIDConnect.InProcess.Configuration;
3+
using AzureFunctions.Extensions.OpenIDConnect.Tests;
34
using FluentAssertions;
45
using Microsoft.Azure.WebJobs.Host;
56
using Microsoft.Extensions.DependencyInjection;
67
using NUnit.Framework;
78

8-
namespace AzureFunctions.Extensions.OpenIDConnect.Tests
9+
namespace AzureFunctions.Extensions.OpenIDConnect.InProcess.Tests
910
{
1011
[TestFixture]
1112
public class DependencyInjectionShould

src/AzureFunctions.Extensions.OpenIDConnect.Tests/RouteGuardianShould.cs renamed to src/AzureFunctions.Extensions.OpenIDConnect.InProcess.Tests/RouteGuardianShould.cs

+6-6
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
using Microsoft.Extensions.Logging;
88
using NUnit.Framework;
99

10-
namespace AzureFunctions.Extensions.OpenIDConnect.Tests
10+
namespace AzureFunctions.Extensions.OpenIDConnect.InProcess.Tests
1111
{
1212
using System;
1313
using System.Collections.Generic;
@@ -19,7 +19,7 @@ public class RouteGuardianShould
1919
public void Not_Authorize_When_Not_HttpTrigger()
2020
{
2121
// Arrange
22-
var guardian = new RouteGuardian(() => new List<Type>{ typeof(Not_HttpTrigger) });
22+
var guardian = new RouteGuardian(() => new List<Type> { typeof(Not_HttpTrigger) }, new FunctionsAnalyzer());
2323

2424
// Act
2525
var result = guardian.IsProtectedRoute("Not_HttpTrigger");
@@ -32,7 +32,7 @@ public void Not_Authorize_When_Not_HttpTrigger()
3232
public void Not_Authorize_When_No_Authorize_Attribute_On_Method_And_Type()
3333
{
3434
// Arrange
35-
var guardian = new RouteGuardian(() => new List<Type> { typeof(No_Authorize_Attribute_On_Method_And_Type) });
35+
var guardian = new RouteGuardian(() => new List<Type> { typeof(No_Authorize_Attribute_On_Method_And_Type) }, new FunctionsAnalyzer());
3636

3737
// Act
3838
var result = guardian.IsProtectedRoute("No_Authorize_Attribute_On_Method_And_Type");
@@ -45,7 +45,7 @@ public void Not_Authorize_When_No_Authorize_Attribute_On_Method_And_Type()
4545
public void Authorize_When_Authorize_Attribute_Is_On_Method()
4646
{
4747
// Arrange
48-
var guardian = new RouteGuardian(() => new List<Type> { typeof(Authorize_Attribute_Is_On_Method) });
48+
var guardian = new RouteGuardian(() => new List<Type> { typeof(Authorize_Attribute_Is_On_Method) }, new FunctionsAnalyzer());
4949

5050
// Act
5151
var result = guardian.IsProtectedRoute("Authorize_Attribute_Is_On_Method");
@@ -58,7 +58,7 @@ public void Authorize_When_Authorize_Attribute_Is_On_Method()
5858
public void Authorize_When_Authorize_Attribute_Is_On_Class()
5959
{
6060
// Arrange
61-
var guardian = new RouteGuardian(() => new List<Type> { typeof(Authorize_Attribute_Is_On_Class) });
61+
var guardian = new RouteGuardian(() => new List<Type> { typeof(Authorize_Attribute_Is_On_Class) }, new FunctionsAnalyzer());
6262

6363
// Act
6464
var result = guardian.IsProtectedRoute("Authorize_Attribute_Is_On_Class");
@@ -71,7 +71,7 @@ public void Authorize_When_Authorize_Attribute_Is_On_Class()
7171
public void NotAuthorize_When_Authorize_Attribute_Is_On_Class_But_AllowAnonimous_On_Method()
7272
{
7373
// Arrange
74-
var guardian = new RouteGuardian(() => new List<Type> { typeof(Attribute_Is_On_Class_But_AllowAnonimous_On_Method) });
74+
var guardian = new RouteGuardian(() => new List<Type> { typeof(Attribute_Is_On_Class_But_AllowAnonimous_On_Method) }, new FunctionsAnalyzer());
7575

7676
// Act
7777
var result = guardian.IsProtectedRoute("Attribute_Is_On_Class_But_AllowAnonimous_On_Method");

src/AzureFunctions.Extensions.OpenIDConnect.Tests/TokenValidationShould.cs renamed to src/AzureFunctions.Extensions.OpenIDConnect.InProcess.Tests/TokenValidationShould.cs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
namespace AzureFunctions.Extensions.OpenIDConnect.Tests
1+
using AzureFunctions.Extensions.OpenIDConnect.Tests;
2+
3+
namespace AzureFunctions.Extensions.OpenIDConnect.InProcess.Tests
24
{
35
using Configuration;
46
using FluentAssertions;
@@ -40,7 +42,7 @@ public void BeSecure_When_Using_Audience_And_Issuer()
4042

4143
// Act
4244
var tokenValidationParameters = provider.GetService<TokenValidationParameters>();
43-
45+
4446
// Assert
4547
tokenValidationParameters.Should().BeEquivalentTo(expected);
4648
}

src/AzureFunctions.Extensions.OpenIDConnect/AuthorizeFilter.cs renamed to src/AzureFunctions.Extensions.OpenIDConnect.InProcess/AuthorizeFilter.cs

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
using System;
2+
using System.Net;
3+
using System.Threading;
4+
using System.Threading.Tasks;
25
using Microsoft.AspNetCore.Authorization;
6+
using Microsoft.AspNetCore.Http;
7+
using Microsoft.Azure.WebJobs.Host;
38

4-
namespace AzureFunctions.Extensions.OpenIDConnect
9+
namespace AzureFunctions.Extensions.OpenIDConnect.InProcess
510
{
6-
using System.Net;
7-
using System.Threading;
8-
using System.Threading.Tasks;
9-
using Microsoft.AspNetCore.Http;
10-
using Microsoft.Azure.WebJobs.Host;
11-
1211
public class AuthorizeFilter : FunctionInvocationFilterAttribute
1312
{
1413
private readonly IHttpContextAccessor _httpContextAccessor;
@@ -54,6 +53,7 @@ public override async Task OnExecutingAsync(FunctionExecutingContext executingCo
5453
if (!authorizationResult.Succeeded)
5554
{
5655
await Forbidden(httpContext, authorizationResult.Failure, cancellationToken);
56+
return;
5757
}
5858
}
5959

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFramework>netstandard2.0</TargetFramework>
4+
<PublisherName>Fabrice Michellonet</PublisherName>
5+
<PackageId>AzureFunctions.Extensions.OpenIDConnect.InProcess</PackageId>
6+
<Version>1.0.0.0</Version>
7+
<Copyright>Fabrice Michellonet 2021</Copyright>
8+
<Title>In process Azure Functions Authentication</Title>
9+
<Description>
10+
OIDC / JWT Bearer Tokens authentication made easy for in-process Azure Functions.
11+
Works with the well known ASPNET [Authorize] attribute.
12+
Works with popular identity providers including Auth0, Azure AD B2C, Azure AD, Google and Okta.
13+
</Description>
14+
<Authors>Fabrice Michellonet</Authors>
15+
<RepositoryType>git</RepositoryType>
16+
<RepositoryUrl>git://github.com/fmichellonet/AzureFunctions.Extensions.OpenIDConnect</RepositoryUrl>
17+
<PackageReleaseNotes></PackageReleaseNotes>
18+
<PackageProjectUrl>https://github.com/fmichellonet/AzureFunctions.Extensions.OpenIDConnect</PackageProjectUrl>
19+
<PackageIcon>icon.png</PackageIcon>
20+
<PackageTags>Azure Functions;Token Validation;JWT;OIDC;Auth0;Azure AD B2C;Azure AD;Okta;Google</PackageTags>
21+
22+
<!-- Optional: Publish the repository URL in the built .nupkg (in the NuSpec <Repository> element) -->
23+
<PublishRepositoryUrl>true</PublishRepositoryUrl>
24+
25+
<!-- Optional: Embed source files that are not tracked by the source control manager in the PDB -->
26+
<EmbedUntrackedSources>true</EmbedUntrackedSources>
27+
28+
<!-- Optional: Build symbol package (.snupkg) to distribute the PDB containing Source Link -->
29+
<IncludeSymbols>true</IncludeSymbols>
30+
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
31+
32+
</PropertyGroup>
33+
34+
<ItemGroup>
35+
<None Include="..\AzureFunctions.Extensions.OpenIDConnect\icon.png">
36+
<Pack>True</Pack>
37+
<PackagePath>\</PackagePath>
38+
</None>
39+
</ItemGroup>
40+
41+
42+
43+
<ItemGroup>
44+
<PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.1.0" />
45+
</ItemGroup>
46+
47+
<ItemGroup>
48+
<ProjectReference Include="..\AzureFunctions.Extensions.OpenIDConnect\AzureFunctions.Extensions.OpenIDConnect.csproj" />
49+
</ItemGroup>
50+
51+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using System;
2+
using AzureFunctions.Extensions.OpenIDConnect.Configuration;
3+
using Microsoft.AspNetCore.Authorization;
4+
using Microsoft.Azure.WebJobs.Host;
5+
using Microsoft.Extensions.DependencyInjection;
6+
7+
namespace AzureFunctions.Extensions.OpenIDConnect.InProcess.Configuration
8+
{
9+
public static class ServicesConfigurationExtensions
10+
{
11+
public static void AddOpenIDConnect(this IServiceCollection services, string issuer, string audience)
12+
{
13+
RegisterServices(services);
14+
ServicesConfigurationExtensionsBase.AddOpenIDConnect(services, issuer, audience);
15+
}
16+
17+
public static void AddOpenIDConnect(this IServiceCollection services, Action<ConfigurationBuilder> configurator)
18+
{
19+
RegisterServices(services);
20+
ServicesConfigurationExtensionsBase.AddOpenIDConnect(services, configurator);
21+
}
22+
23+
private static void RegisterServices(IServiceCollection services)
24+
{
25+
services.AddHttpContextAccessor();
26+
services.AddSingleton<IAuthorizationService, DefaultAuthorizationService>();
27+
services.AddSingleton<IAuthorizationPolicyProvider, DefaultAuthorizationPolicyProvider>();
28+
services.AddSingleton<IAuthorizationHandlerProvider, DefaultAuthorizationHandlerProvider>();
29+
services.AddSingleton<IFunctionsAnalyzer, FunctionsAnalyzer>();
30+
services.AddSingleton<IFunctionFilter, AuthorizeFilter>();
31+
}
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System.Linq;
2+
using Microsoft.Azure.WebJobs;
3+
using System.Reflection;
4+
5+
namespace AzureFunctions.Extensions.OpenIDConnect.InProcess
6+
{
7+
public class FunctionsAnalyzer : IFunctionsAnalyzer
8+
{
9+
public bool IsAzureFunction(MethodInfo methodInfo)
10+
{
11+
return methodInfo.GetCustomAttributes<FunctionNameAttribute>().Any();
12+
}
13+
14+
public bool IsHttpTrigger(MethodInfo methodInfo)
15+
{
16+
return methodInfo.GetParameters()
17+
.Any(paramInfo => paramInfo.GetCustomAttributes<HttpTriggerAttribute>().Any());
18+
}
19+
20+
public string GetRoute(MethodInfo methodInfo)
21+
{
22+
return methodInfo.GetParameters()
23+
.SelectMany(paramInfo => paramInfo.GetCustomAttributes<HttpTriggerAttribute>())
24+
.Select(x => x.Route)
25+
.First();
26+
}
27+
28+
public string GetFunctionName(MethodInfo methodInfo)
29+
{
30+
return methodInfo.GetCustomAttributes<FunctionNameAttribute>()
31+
.Select(x => x.Name)
32+
.First();
33+
}
34+
}
35+
}

0 commit comments

Comments
 (0)