Smartersoft.Azure.WebJobs.Extensions.Jwt 0.1.5-beta0011

This is a prerelease version of Smartersoft.Azure.WebJobs.Extensions.Jwt.
There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package Smartersoft.Azure.WebJobs.Extensions.Jwt --version 0.1.5-beta0011                
NuGet\Install-Package Smartersoft.Azure.WebJobs.Extensions.Jwt -Version 0.1.5-beta0011                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Smartersoft.Azure.WebJobs.Extensions.Jwt" Version="0.1.5-beta0011" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Smartersoft.Azure.WebJobs.Extensions.Jwt --version 0.1.5-beta0011                
#r "nuget: Smartersoft.Azure.WebJobs.Extensions.Jwt, 0.1.5-beta0011"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Smartersoft.Azure.WebJobs.Extensions.Jwt as a Cake Addin
#addin nuget:?package=Smartersoft.Azure.WebJobs.Extensions.Jwt&version=0.1.5-beta0011&prerelease

// Install Smartersoft.Azure.WebJobs.Extensions.Jwt as a Cake Tool
#tool nuget:?package=Smartersoft.Azure.WebJobs.Extensions.Jwt&version=0.1.5-beta0011&prerelease                

Smartersoft.Azure.WebJobs.Extensions.Jwt

Nuget package License badge GitHub issues Source

GitHub Sponsors Follow on Twitter

Azure Functions is great for creating an api easily, we felt it was missing support for JWT authentication. So we build this small extension to get jwt support backed by OpenID connect discovery for automatic key rollover.

We build this to support Azure AD, you can however use it with whatever identity provider as long as it supports OpenID connect (which I'm sure all do these days).

This library is created by Smartersoft B.V. and licensed as GPL-3.0-only.

Getting started

Create new Azure Functions or use an existing one. This package supports net7.0, net6.0 and netcoreapp3.1.

Add packages Smartersoft.Azure.WebJobs.Extensions.Jwt to your project.

Project file changes

Add the following line to your Functions project file.

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <AzureFunctionsVersion>v4</AzureFunctionsVersion>
    
    <_FunctionsSkipCleanOutput>true</_FunctionsSkipCleanOutput>
  </PropertyGroup>

Required configuration

Add this information to your local.settings.json file replace the fields accordingly:

  "JWT__Authority": "https://login.microsoftonline.com/{your-tenant-id}/v2.0",
  "JWT__ValidAudiences__0": "{application-id}",
  "JWT__ValidAudiences__1": "api://{application-id-as-app-uri}",

You can replace {your-tenant-id} with organizations to support all organizations. For single tenant usage be sure to also add:

  "JWT__ValidIssuers__0": "https://login.microsoftonline.com/{your-tenant-id}/v2.0",

These properties are in the JWT configuration section (because of the double underscores), and will be needed in the Startup file.

The configuration will be downloaded from {Authority}/.well-known/openid-configuration.

Startup file

This library extends the Azure Functions, follow use DI in .NET Azure Functions to set up Dependency injection correctly.

Add the following to your startup file:

using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Smartersoft.Azure.WebJobs.Extensions.Jwt;
[assembly: FunctionsStartup(typeof(Startup))]
public class Startup : FunctionsStartup
{
  public override void Configure(IFunctionsHostBuilder builder)
  {
    ...
    builder.AddOpenIdConnectTokenValidator();
    // or if you changed the Authentication section name to Auth
    // builder.AddOpenIdConnectTokenValidator("Auth");
    ...
  }
}

WebJobs extension

Create a StartupWebJobs class, with the following content:

using Microsoft.Azure.WebJobs.Hosting;
using Microsoft.Azure.WebJobs;
using Smartersoft.Azure.WebJobs.Extensions.Jwt;
[assembly: WebJobsStartup(typeof(StartupWebJobs))]
public class StartupWebJobs : IWebJobsStartup
{
  public void Configure(IWebJobsBuilder builder)
  {
    builder.AddTokenValidatorExtension();
  }
}

Azure AD configuration

  1. Create an app registration for your API guide
  2. Expose a scope for your api guide (for delegated permissions) user_impersonation in this sample.
  3. Add app roles for you API guide (for application permissions) Read.All in this sample
  4. Create a client application to get tokens with.

Tokens in a delegated (user) flow, will only be given out once consent is given.

Token in an application flow, will be given to any application in the tenant, even if they are not granted admin consent. These tokens will however not contain any role, which is why it's really important to verify at least a scope or a role. This library enforces that, we don't want you to become vulnerable to incorrect configuration attacks.

Validate tokens

Add [TokenValidator(Scopes = "user_impersonation")] TokenResult token to your function. And use this code to return an error:

if (token.Status != TokenStatus.Valid)
{
  return token.GetFaultyActionResult();
}

Validate tokens NOTE

We know this is not ideal since having to verify the token in each function has several down-sides:

  • Developer can forget to check the token, make sure you test all your endpoints for this!
  • You can forget to emit a faulty result.

We use this way because of the following reasons:

  • This gives you total control on what statuscode to emit.
  • You can use all the details in the token right in your application.
  • The correct error code shows up in the Azure Functions logging.

An other way would be trowing an exception upon a faulty or missing token, but since those would then be binding errors it would throw a 500 error because a value could not be bound correctly. And you cannot catch binding errors in Azure functions.

Sample function

using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using Smartersoft.Azure.WebJobs.Extensions.Jwt;

namespace MyFunctions;

public class CheckTokenFunction
{
  private readonly ILogger<CheckTokenFunction> _logger;

  public CheckTokenFunction(ILogger<CheckTokenFunction> log)
  {
    _logger = log;
  }

  [FunctionName("CheckTokenFunction")]
  [OpenApiOperation(operationId: "Run", tags: new[] { "name" })]
  [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "text/plain", bodyType: typeof(string), Description = "The OK response")]
  public async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]HttpRequest req,
    [TokenValidator(Scopes = "user_impersonation")] TokenResult token)
  {
    _logger.LogInformation("C# HTTP trigger function processed a request.");

    if (token.Status != TokenStatus.Valid)
    {
      return token.GetFaultyActionResult();
    }

    // Use details from the token
    string name = token.DisplayName;
    string responseMessage = $"Hello, {name}. This HTTP triggered function executed successfully.";

    return new OkObjectResult(responseMessage);
  }
}

License

These packages are licensed under GPL-3.0, if you wish to use this software under a different license. Or you feel that this really helped in your commercial application and wish to support us? You can get in touch and we can talk terms. We are available as consultants.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp3.1 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.1.5-beta0017 82 4/14/2024
0.1.5-beta0016 81 1/23/2024
0.1.5-beta0011 197 11/9/2022
0.1.5-beta0010 135 11/9/2022