This page describes the advanced configurations for OpenAPI and Swagger UI in general. If you look for process-specific configurations, please find the following pages:
- Microsoft.Azure.WebJobs.Extensions.OpenApi – In-Process Model
- Microsoft.Azure.Functions.Worker.Extensions.OpenApi – Out-of-Process Model
For the extension's advanced configuration, it expects the following config keys.
NOTE: Currently, the out-of-process worker extension only supports the
AuthorizationLevel.Anonymous
access. The following configurations are only applicable to the in-process worker extension.
As a default, all endpoints to render Swagger UI and OpenAPI documents have the authorisation level of AuthorizationLevel.Anonymous
. However, if you want to secure those endpoints, change their authorisation level to AuthorizationLevel.Function
and pass the API Key through either request header or querystring parameter. This can be done through the environment variables. Here's the sample local.settings.json
file. The other values are omitted for brevity.
{
"Values": {
"OpenApi__ApiKey": "",
"OpenApi__AuthLevel__Document": "Anonymous",
"OpenApi__AuthLevel__UI": "Anonymous"
}
}
You can have granular controls to both Swagger UI and OpenAPI documents by setting the authorisation level to Anonymous
, User
, Function
, System
or Admin
. Make sure that you MUST provide the OpenApi__ApiKey
value, if you choose the OpenApi__AuthLevel__Document
value other than Anonymous
. Otherwise, it will throw an error.
NOTE: Both Swagger UI and OpenAPI document pages are allowed
Anonymous
access by default.
NOTE: Currently, the out-of-process worker model doesn't support hiding Swagger UI. The following configurations are only applicable to the in-process worker extension.
You may want to only enable the Swagger UI page during the development time, and disable the page when publishing it to Azure. You can configure an environment variable to enable/disable the Swagger UI page. Here's the sample local.settings.json
file. The other values are omitted for brevity.
{
"Values": {
"OpenApi__HideSwaggerUI": "false"
}
}
If you set the OpenApi__HideSwaggerUI
value to true
, the Swagger UI page won't be showing up, and you will see the 404 error.
NOTE: The default value for
OpenApi__HideSwaggerUI
isfalse
.
NOTE: Currently, the out-of-process worker model doesn't support hiding OpenAPI document. The following configurations are only applicable to the in-process worker extension.
You may want to only enable the OpenAPI document page during the development time, and disable the page when publishing it to Azure. You can configure an environment variable to enable/disable the OpenAPI document page. Here's the sample local.settings.json
file. The other values are omitted for brevity.
{
"Values": {
"OpenApi__HideDocument": "false"
}
}
If you set the OpenApi__HideDocument
value to true
, the OpenAPI page won't be showing up, and you will see the 404 error. Make sure that, if you set the OpenApi__HideDocument
value to true
, it won't show the Swagger UI page either, regardless the OpenApi__HideSwaggerUI
value is true
or false
.
NOTE: The default value for
OpenApi__HideDocument
isfalse
.
As a default, the OpenAPI document automatically generated provides a minimum set of information like:
- OpenAPI Version:
2.0
- OpenAPI Document Title:
OpenAPI Document on Azure Functions
- OpenAPI Document Version:
1.0.0
You may want to provide consumers with more details by implementing the IOpenApiConfigurationOptions
interface or inheriting the DefaultOpenApiConfigurationOptions
class. If you inherit the DefaultOpenApiConfigurationOptions
class, you can use the following environment variables to avoid the app from being recompiled and redeployed. Here's the sample local.settings.json
file. The other values are omitted for brevity.
{
"Values": {
"OpenApi__Version": "v2",
"OpenApi__DocTitle": "Azure Functions OpenAPI Extension",
"OpenApi__DocVersion": "1.0.0"
}
}
There's a chance that you want to expose the UI and OpenAPI document through Azure API Management or load balancing services like Azure Front Door. If you inherit the DefaultOpenApiConfigurationOptions
class, you can configure an environment variable to add them. Here's the sample local.settings.json
file. The other values are omitted for brevity.
{
"Values": {
"OpenApi__HostNames": "https://contoso.com/api/,https://fabrikam.com/api/"
}
}
NOTE: This multiple hostnames support feature only works with OpenAPI 3.x, not OpenAPI 2.x.
You can also configure the hostname without having to start with the fully-qualified domain name. Here's an example:
{
"Values": {
"OpenApi__HostNames": "/api"
}
}
There's a chance if you want to force the Swagger UI to render either HTTP or HTTPS. If you inherit the DefaultOpenApiConfigurationOptions
class, you can configure environment variables to add them. Here's the sample local.settings.json
file. The other values are omitted for brevity.
NOTE: Both values are set to
false
by default.
{
"Values": {
"OpenApi__ForceHttps": "true",
"OpenApi__ForceHttp": "false",
}
}
NOTE: If your Azure Functions app is running on the Linux Dedicated Plan, consider this configuration.
You may want to selectively display endpoints on the Swagger UI and OpenAPI documents. In this case, you can use the tags to filter which endpoints you want to show. For example, you've got endpoints like:
[FunctionName("MyAdminFunction1")]
[OpenApiOperation(operationId: ..., tags: new[] { "admin" })]
...
public static async Task<IActionResult> MyAdminFunction1(...)
{
...
}
[FunctionName("MyAdminFunction2")]
[OpenApiOperation(operationId: ..., tags: new[] { "admin" })]
...
public static async Task<IActionResult> MyAdminFunction2(...)
{
...
}
[FunctionName("MyFunction1")]
[OpenApiOperation(operationId: ..., tags: new[] { "product" })]
...
public static async Task<IActionResult> MyFunction1(...)
{
...
}
[FunctionName("MyFunction2")]
[OpenApiOperation(operationId: ..., tags: new[] { "product" })]
...
public static async Task<IActionResult> MyFunction2(...)
{
...
}
[FunctionName("MyOptionFunction1")]
[OpenApiOperation(operationId: ..., tags: new[] { "option" })]
...
public static async Task<IActionResult> MyOptionFunction1(...)
{
...
}
If you only want to only show the admin API endpoints, add tag=admin
to the querystring:
http://localhost:7071/api/swagger/ui?tag=admin
http://localhost:7071/api/swagger.json?tag=admin
The tag
parameter accepts a commma separated list of tags. Any function with any tag passed on the tag
parameter will be selected.
If you only want to show the API endpoints related to product
or option
tag, add tag=product,option
to the querystring:
http://localhost:7071/api/swagger/ui?tag=product,option
http://localhost:7071/api/swagger.json?tag=product,option
If the generated document needs to be modified in more complex ways, you can use an IDocumentFilter
that can modify the Swagger and OpenAPI documents just before it is rendered to the client. To register such a filter you must create a class that inherits from the DefaultOpenApiConfigurationOptions
class and add your IDocumentFilter
to its DocumentFilters
list:
public class OpenApiConfigurationOptions : DefaultOpenApiConfigurationOptions
{
public OpenApiConfigurationOptions()
{
this.DocumentFilters.Add(new ExampleDocumentFilter());
}
}
This code adds the ExampleDocumentFilter
class to the list of document filters. Within a document filter you access to an IHttpRequestDataObject
object, which contains request data like the current host and scheme, and to the OpenApiDocument
object which contains all the generated documentation.
public class ExampleDocumentFilter : IDocumentFilter
{
public void Apply(IHttpRequestDataObject request, OpenApiDocument document)
{
// TO DO
}
}
This extension relies on the built-in authentication method, either code=
in the querystring or x-functions-key
in the request header. However, if you want additional authentication and authorisation for both Swagger UI and OpenAPI documents, you can implement the IOpenApiHttpTriggerAuthorization
interface or inherit the DefaultOpenApiHttpTriggerAuthorization
class.
Here's the DefaultOpenApiHttpTriggerAuthorization
class implementing the IOpenApiHttpTriggerAuthorization
interface.
public class DefaultOpenApiHttpTriggerAuthorization : IOpenApiHttpTriggerAuthorization
{
public virtual async Task<OpenApiAuthorizationResult> AuthorizeAsync(IHttpRequestDataObject req)
{
var result = default(OpenApiAuthorizationResult);
return await Task.FromResult(result).ConfigureAwait(false);
}
}
If you want your own implementation with OAuth2, you may like to do like:
public class MyOpenApiHttpTriggerAuthorization : DefaultOpenApiHttpTriggerAuthorization
{
public override async Task<OpenApiAuthorizationResult> AuthorizeAsync(IHttpRequestDataObject req)
{
var result = default(OpenApiAuthorizationResult);
var authtoken = (string) req.Headers["Authorization"];
// Redirect to the auth page, if no auth header is found.
if (authtoken.IsNullOrWhiteSpace())
{
result = new OpenApiAuthorizationResult()
{
StatusCode = HttpStatusCode.Redirect,
ContentType = "text/html",
Payload = "<html><head><meta http-equiv=\"refresh\" content=\"0;url=https://login.microsoftonline.com/common/oauth2/v2.0/authorize\"/></head><body></body></html>",
};
return await Task.FromResult(result).ConfigureAwait(false);
}
// Shows invalid auth format, if the auth header is not the bearer token.
if (authtoken.StartsWith("Bearer", ignoreCase: true, CultureInfo.InvariantCulture) == false)
{
result = new OpenApiAuthorizationResult()
{
StatusCode = HttpStatusCode.Unauthorized,
ContentType = "text/plain",
Payload = "Invalid auth format",
};
return await Task.FromResult(result).ConfigureAwait(false);
}
// Shows invalid auth token, if token value is invalid.
var token = authtoken.Split(' ').Last();
if (token != "secret")
{
result = new OpenApiAuthorizationResult()
{
StatusCode = HttpStatusCode.Forbidden,
ContentType = "text/plain",
Payload = "Invalid auth token",
};
return await Task.FromResult(result).ConfigureAwait(false);
}
// DO SOMETHING IF AUTH TOKEN IS VALID.
...
// Return null if auth is valid.
return await Task.FromResult(result).ConfigureAwait(false);
}
}
You may want to inject the OpenApiHttpTriggerAuthorization
instance during startup:
Then, OpenApiHttpTriggerContext
automatically picks it up and invokes its AuthorizeAsync(...)
method. The following code shows how your auth results are handled within each Swagger UI and OpenAPI document endpoints.
// In-Process Worker
var auth = await context.SetApplicationAssemblyAsync(ctx.FunctionAppDirectory)
.AuthorizeAsync(request)
.ConfigureAwait(false);
if (!auth.IsNullOrDefault())
{
content = new ContentResult()
{
Content = auth.Payload,
ContentType = auth.ContentType,
StatusCode = (int)auth.StatusCode,
};
return content;
}
// Out-Of-Process Worker
var auth = await context.SetApplicationAssemblyAsync(fi.Directory.FullName, appendBin: false)
.AuthorizeAsync(request)
.ConfigureAwait(false);
if (!auth.IsNullOrDefault())
{
response = req.CreateResponse(auth.StatusCode);
response.Headers.Add("Content-Type", auth.ContentType);
await response.WriteStringAsync(auth.Payload).ConfigureAwait(false);
return response;
}
There are more authN scenarios described in this document, Securing Azure Functions Endpoints through OpenAPI Auth. Take a look for more details.