From 79f07e5c3a53be79f27b8e07725bcf24621273db Mon Sep 17 00:00:00 2001 From: "Daniel G. Taylor" Date: Tue, 9 Jan 2024 10:04:07 -0800 Subject: [PATCH 01/11] docs: Add OAuth 2.0 & JWT how-to --- docs/docs/how-to/oauth2-jwt.md | 201 +++++++++++++++++++++++++++++++++ docs/mkdocs.yml | 1 + 2 files changed, 202 insertions(+) create mode 100644 docs/docs/how-to/oauth2-jwt.md diff --git a/docs/docs/how-to/oauth2-jwt.md b/docs/docs/how-to/oauth2-jwt.md new file mode 100644 index 00000000..7a134a33 --- /dev/null +++ b/docs/docs/how-to/oauth2-jwt.md @@ -0,0 +1,201 @@ +--- +description: Setting up OAuth2.0 JWT authentication for your API. +--- + +## Overview + +[OAuth 2.0](https://oauth.net/2/) is a popular open standard authentication & authorization framework that enables you to authorize requests to your API. There are three main pieces to using OAuth 2.0 with Huma: + +1. Issue a token (JWT) to a user +2. Document the auth scheme and required permissions +3. Authorize incoming requests + +## Issue a Token (JWT) + +Huma does not provide any built-in token issuing functionality. Instead, you can use any existing library or service to issue tokens. For simplicity sake, we will assume you are using a third-party service for managing users and issuing tokens, like [Auth0](https://auth0.com/) or [Okta](https://www.okta.com/). It looks something like this: + +```mermaid +graph LR + User -->|1. Login| Auth0 + Auth0 -->|2. Issue JWT| User + Auth0 -.->|Refresh JWKS| API + User --->|3. Make request| API + API -->|4. Verify JWT & Perms| Validate + Validate -->|5. Accept/reject| API + API --->|6. Success| Handler +``` + +You will configure the third-party service to issue JWTs from OAuth 2.0 flows like Authorization Code or Client Credentials (among others) and will be given e.g. authorization and token URLs, which will be used later in the OpenAPI and to configure clients to fetch JWTs. + +If **not** using a third-party service, you will need to set up a signing authority, publish your own JWKS, and issue short-lived tokens yourself. This is outside the scope of this guide, but take a look at [github.com/lestrrat-go/jwx](https://github.com/lestrrat-go/jwx) for a library that can help. + +## Document the Auth Scheme in OpenAPI + +Next, you need to document the auth scheme in your OpenAPI document. This is done using the `SecuritySchemes` component. Here is an example defining an OAuth 2.0 Authorization Code flow with the URLs mentioned above and a couple of defined scopes: + +```go title="main.go" +router := chi.NewMux() + +config := huma.DefaultConfig("My API", "1.0.0") +config.Components.SecuritySchemes = map[string]*huma.SecurityScheme{ + // Example Authorization Code flow. + "myAuth": { + Type: "oauth2", + Flows: &huma.OAuthFlows{ + AuthorizationCode: &huma.OAuthFlow{ + AuthorizationURL: "https://example.com/oauth/authorize", + TokenURL: "https://example.com/oauth/token", + Scopes: map[string]string{ + "scope1": "Scope 1 description...", + "scope2": "Scope 2 description...", + }, + }, + }, + }, + + // Example alternative describing the use of JWTs without documenting how + // they are issued or which flows might be supported. This is simpler but + // tells clients less information. + "anotherAuth": { + Type: "http", + Scheme: "bearer", + BearerFormat: "JWT", + }, +} + +api := humachi.New(router, config) +``` + +When registering your operation you can refer to the auth scheme and required scopes for that operation: + +```go title="main.go" hl_lines="6-8" +huma.Register(api, huma.Operation{ + OperationID: "get-greeting", + Summary: "Get a greeting", + Method: http.MethodGet, + Path: "/greeting/{name}", + Security: []map[string][]string{ + {"myAuth": {"scope1"}}, + }, +}, func(ctx context.Context, input *GreetingInput) (*GreetingOutput, error) { + // TODO: operation implementation goes here + return nil, nil +}) +``` + +!!! warning "Documentation Only" + + This is documenting the auth mechanism and required scopes, but does not actually authorize incoming requests. See the next section for how to do that. + +## Authorize Incoming Requests + +Where authentication & authorization happen depends on how your service is set up. In some scenarios you may have an API gateway that handles auth and forwards requests to your service. In other scenarios you may want to handle auth in your service. + +### API Gateway Auth + +In an API gateway scenario, you typically configure the gateway to check the `Authorization` header for a token and validate it against the JWKS URL. If the token is valid, then the gateway will forward the request to your API service. There are many such gateways (e.g. [Traefik](https://traefik.io/traefik/), [Istio](https://istio.io/), etc) and ways of configuring them, but the general idea is similar between them: + +```mermaid +graph LR + APIGateway[API Gateway] + AuthMiddleware[Auth Middleware] + + User -->|Request| APIGateway + APIGateway --> AuthMiddleware + AuthMiddleware --> APIGateway + APIGateway --->|Forward| API +``` + +In this case, you can skip this section as your service will never see unauthorized requests and the Huma code above acts mostly as documentation for your clients. + +### Huma Auth Middleware + +Huma provides middleware functionality that can be used to authorize incoming requests within the API service itself. Here is an example that will check the `Authorization` header for a token and validate it against the JWKS URL given by your JWT issuer (e.g. Auth0/Okta). It will also check that the token has the required scopes for the operation, if any are defined. + +```go title="main.go" +import ( + "github.com/lestrrat-go/jwx/v2/jwk" + "github.com/lestrrat-go/jwx/v2/jwt" +) + +// NewJWKSet creates an auto-refreshing key set to validate JWT signatures. +func NewJWKSet(jwkUrl string) jwk.Set { + jwkCache := jwk.NewCache(context.Background()) + + // register a minimum refresh interval for this URL. + // when not specified, defaults to Cache-Control and similar resp headers + err := jwkCache.Register(jwkUrl, jwk.WithMinRefreshInterval(10*time.Minute)) + if err != nil { + panic("failed to register jwk location") + } + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // fetch once on application startup + _, err = jwkCache.Refresh(ctx, jwkUrl) + if err != nil { + panic("failed to fetch on startup") + } + // create the cached key set + return jwk.NewCachedSet(jwkCache, jwkUrl) +} + +// NewAuthMiddleware creates a middleware that will authorize requests based on +// the required scopes for the operation. +func NewAuthMiddleware(jwksURL string) { + keySet := NewJWKSet(jwksURL) + + return func(ctx huma.Context, next func(huma.Context)) { + var anyOfNeededRoles []string + isAuthorizationRequired := false + for _, opScheme := range ctx.Operation().Security { + var ok bool + if anyOfNeededRoles, ok = opScheme["myAuth"]; ok { + isAuthorizationRequired = true + break + } + } + + if !isAuthorizationRequired { + next(ctx) + return + } + + token := strings.TrimPrefix(ctx.Header("Authorization"), "Bearer ") + + // Parse and validate the JWT. + parsed, err := jwt.ParseString(token, + jwt.WithKeySet(keySet), + jwt.WithValidate(true), + jwt.WithIssuer("my-issuer"), + jwt.WithAudience("my-audience"), + ) + + // Ensure the claims required for this operation are present. + scopes = parsed.Get("scopes").([]string) + for _ scope := range scopes { + if slices.Contains(anyOfNeededRoles, scope) { + next(ctx) + return + } + } + + huma.WriteErr(ctx, http.StatusUnauthorized, "Unauthorized") + } +} +``` + +Lastly, when configuring your API, be sure to include this middleware: + +```go title="main.go" +api.UseMiddleware(NewAuthMiddleware("https://example.com/.well-known/jwks.json")) +``` + +## Optional: Client Auto-Configuration + +Some clients like [Restish](https://rest.sh/) support [OpenAPI-based auth auto-configuration](https://rest.sh/#/openapi?id=autoconfiguration). This means that you can configure your client to fetch the OpenAPI document and automatically configure itself to use the correct auth mechanism. This is done by adding the `x-cli-config` extension to the OpenAPI: + +```go title="main.go" +config.Extensions["x-cli-config"] = huma.AutoConfig{ /* ... */ } +``` diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 21f8ec01..acfd7311 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -25,6 +25,7 @@ nav: - "Image Response": how-to/image-response.md - "Graceful Shutdown": how-to/graceful-shutdown.md - "Migrating From V1": how-to/migrate.md + - "OAuth 2.0 & JWT": how-to/oauth2-jwt.md - "Features": - "Features Overview": features/index.md - "The Server": From db70366fcc86ee55f92784af3e48797d4394bf09 Mon Sep 17 00:00:00 2001 From: "Daniel G. Taylor" Date: Thu, 11 Jan 2024 10:17:35 -0800 Subject: [PATCH 02/11] Update docs/docs/how-to/oauth2-jwt.md Co-authored-by: Stefan Moser <6841360+sm3142@users.noreply.github.com> --- docs/docs/how-to/oauth2-jwt.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/docs/how-to/oauth2-jwt.md b/docs/docs/how-to/oauth2-jwt.md index 7a134a33..9b051584 100644 --- a/docs/docs/how-to/oauth2-jwt.md +++ b/docs/docs/how-to/oauth2-jwt.md @@ -4,7 +4,9 @@ description: Setting up OAuth2.0 JWT authentication for your API. ## Overview -[OAuth 2.0](https://oauth.net/2/) is a popular open standard authentication & authorization framework that enables you to authorize requests to your API. There are three main pieces to using OAuth 2.0 with Huma: +[OAuth 2.0](https://oauth.net/2/) is a popular open standard authorization framework that enables you to verify that incoming requests are authorized to use your API. + +There are three main pieces to using OAuth 2.0 with Huma: 1. Issue a token (JWT) to a user 2. Document the auth scheme and required permissions From cd97545866d754624b69614c2159c103b164c89f Mon Sep 17 00:00:00 2001 From: "Daniel G. Taylor" Date: Thu, 11 Jan 2024 10:17:44 -0800 Subject: [PATCH 03/11] Update docs/docs/how-to/oauth2-jwt.md Co-authored-by: Stefan Moser <6841360+sm3142@users.noreply.github.com> --- docs/docs/how-to/oauth2-jwt.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/how-to/oauth2-jwt.md b/docs/docs/how-to/oauth2-jwt.md index 9b051584..0294b5ee 100644 --- a/docs/docs/how-to/oauth2-jwt.md +++ b/docs/docs/how-to/oauth2-jwt.md @@ -1,5 +1,5 @@ --- -description: Setting up OAuth2.0 JWT authentication for your API. +description: Setting up OAuth2.0 authorization for your API --- ## Overview From b9a0b7358517246db8877c64a675ed0b0960639a Mon Sep 17 00:00:00 2001 From: "Daniel G. Taylor" Date: Thu, 11 Jan 2024 10:17:53 -0800 Subject: [PATCH 04/11] Update docs/docs/how-to/oauth2-jwt.md Co-authored-by: Stefan Moser <6841360+sm3142@users.noreply.github.com> --- docs/docs/how-to/oauth2-jwt.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/how-to/oauth2-jwt.md b/docs/docs/how-to/oauth2-jwt.md index 0294b5ee..574dfb6a 100644 --- a/docs/docs/how-to/oauth2-jwt.md +++ b/docs/docs/how-to/oauth2-jwt.md @@ -8,7 +8,7 @@ description: Setting up OAuth2.0 authorization for your API There are three main pieces to using OAuth 2.0 with Huma: -1. Issue a token (JWT) to a user +1. Issue an access token to a client application 2. Document the auth scheme and required permissions 3. Authorize incoming requests From 421c90d2285bdc6f92565426d0f2ce4209de9312 Mon Sep 17 00:00:00 2001 From: "Daniel G. Taylor" Date: Thu, 11 Jan 2024 10:18:05 -0800 Subject: [PATCH 05/11] Update docs/docs/how-to/oauth2-jwt.md Co-authored-by: Stefan Moser <6841360+sm3142@users.noreply.github.com> --- docs/docs/how-to/oauth2-jwt.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/docs/how-to/oauth2-jwt.md b/docs/docs/how-to/oauth2-jwt.md index 574dfb6a..1aa5ebf4 100644 --- a/docs/docs/how-to/oauth2-jwt.md +++ b/docs/docs/how-to/oauth2-jwt.md @@ -12,20 +12,19 @@ There are three main pieces to using OAuth 2.0 with Huma: 2. Document the auth scheme and required permissions 3. Authorize incoming requests -## Issue a Token (JWT) +## Issue an Access Token -Huma does not provide any built-in token issuing functionality. Instead, you can use any existing library or service to issue tokens. For simplicity sake, we will assume you are using a third-party service for managing users and issuing tokens, like [Auth0](https://auth0.com/) or [Okta](https://www.okta.com/). It looks something like this: +Huma does not provide any built-in access token issuing functionality. Instead, you can use any existing library or service to issue tokens. For simplicity's sake, we will assume you are using a third-party service for managing users and issuing tokens, like [Auth0](https://auth0.com/) or [Okta](https://www.okta.com/).A simplified flow chart for OAuth2.0 authorization looks something like this: ```mermaid graph LR User -->|1. Login| Auth0 - Auth0 -->|2. Issue JWT| User + Auth0 -->|2. Issue access token| User Auth0 -.->|Refresh JWKS| API User --->|3. Make request| API - API -->|4. Verify JWT & Perms| Validate + API -->|4. Verify access token & roles| Validate Validate -->|5. Accept/reject| API API --->|6. Success| Handler -``` You will configure the third-party service to issue JWTs from OAuth 2.0 flows like Authorization Code or Client Credentials (among others) and will be given e.g. authorization and token URLs, which will be used later in the OpenAPI and to configure clients to fetch JWTs. From 1ff6778686a5469b1769b16d2fe69459ed6282c1 Mon Sep 17 00:00:00 2001 From: "Daniel G. Taylor" Date: Thu, 11 Jan 2024 10:18:29 -0800 Subject: [PATCH 06/11] Update docs/docs/how-to/oauth2-jwt.md Co-authored-by: Stefan Moser <6841360+sm3142@users.noreply.github.com> --- docs/docs/how-to/oauth2-jwt.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/docs/how-to/oauth2-jwt.md b/docs/docs/how-to/oauth2-jwt.md index 1aa5ebf4..45fd4d7b 100644 --- a/docs/docs/how-to/oauth2-jwt.md +++ b/docs/docs/how-to/oauth2-jwt.md @@ -84,9 +84,8 @@ huma.Register(api, huma.Operation{ }) ``` -!!! warning "Documentation Only" - - This is documenting the auth mechanism and required scopes, but does not actually authorize incoming requests. See the next section for how to do that. +> [!WARNING] +> So far, the code above is only documenting the authorization scheme and required scopes, but does not actually authorize incoming requests. The next section will explain to achieve the latter. ## Authorize Incoming Requests From 2445ce517b4ca07350c48831a7e8f0917e0c7733 Mon Sep 17 00:00:00 2001 From: "Daniel G. Taylor" Date: Thu, 11 Jan 2024 10:18:48 -0800 Subject: [PATCH 07/11] Update docs/docs/how-to/oauth2-jwt.md Co-authored-by: Stefan Moser <6841360+sm3142@users.noreply.github.com> --- docs/docs/how-to/oauth2-jwt.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/how-to/oauth2-jwt.md b/docs/docs/how-to/oauth2-jwt.md index 45fd4d7b..ebe0f810 100644 --- a/docs/docs/how-to/oauth2-jwt.md +++ b/docs/docs/how-to/oauth2-jwt.md @@ -106,7 +106,7 @@ graph LR APIGateway --->|Forward| API ``` -In this case, you can skip this section as your service will never see unauthorized requests and the Huma code above acts mostly as documentation for your clients. +In this case and depending on your security requirements, you may be able to skip this section since all incoming requests to your API will have already been vetted by the gateway. In this scenario, the Huma code frome the previous section serves mostly as documentation for your clients. ### Huma Auth Middleware From d9b652d5d5973cebfb0f71e3497936ee17161a1f Mon Sep 17 00:00:00 2001 From: "Daniel G. Taylor" Date: Thu, 11 Jan 2024 10:19:15 -0800 Subject: [PATCH 08/11] Update docs/docs/how-to/oauth2-jwt.md Co-authored-by: Stefan Moser <6841360+sm3142@users.noreply.github.com> --- docs/docs/how-to/oauth2-jwt.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/docs/how-to/oauth2-jwt.md b/docs/docs/how-to/oauth2-jwt.md index ebe0f810..46177c5e 100644 --- a/docs/docs/how-to/oauth2-jwt.md +++ b/docs/docs/how-to/oauth2-jwt.md @@ -26,7 +26,9 @@ graph LR Validate -->|5. Accept/reject| API API --->|6. Success| Handler -You will configure the third-party service to issue JWTs from OAuth 2.0 flows like Authorization Code or Client Credentials (among others) and will be given e.g. authorization and token URLs, which will be used later in the OpenAPI and to configure clients to fetch JWTs. +The access token may be issued in different flavors & formats, but for the remainder of this document we will assume they are [JWTs](https://jwt.io/). + +You will configure the third-party service to issue access token from OAuth 2.0 flows like Authorization Code or Client Credentials (among others) and will be given e.g. authorization and token URLs, which will be used later in the OpenAPI and to configure clients to fetch access tokens. If **not** using a third-party service, you will need to set up a signing authority, publish your own JWKS, and issue short-lived tokens yourself. This is outside the scope of this guide, but take a look at [github.com/lestrrat-go/jwx](https://github.com/lestrrat-go/jwx) for a library that can help. From 25bae941b2f1eff5b707af7e1823d3d8f4d50fb0 Mon Sep 17 00:00:00 2001 From: "Daniel G. Taylor" Date: Tue, 16 Jan 2024 20:38:49 -0800 Subject: [PATCH 09/11] Update docs/docs/how-to/oauth2-jwt.md Co-authored-by: Stefan Moser <6841360+sm3142@users.noreply.github.com> --- docs/docs/how-to/oauth2-jwt.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/how-to/oauth2-jwt.md b/docs/docs/how-to/oauth2-jwt.md index 46177c5e..341eedf6 100644 --- a/docs/docs/how-to/oauth2-jwt.md +++ b/docs/docs/how-to/oauth2-jwt.md @@ -14,7 +14,7 @@ There are three main pieces to using OAuth 2.0 with Huma: ## Issue an Access Token -Huma does not provide any built-in access token issuing functionality. Instead, you can use any existing library or service to issue tokens. For simplicity's sake, we will assume you are using a third-party service for managing users and issuing tokens, like [Auth0](https://auth0.com/) or [Okta](https://www.okta.com/).A simplified flow chart for OAuth2.0 authorization looks something like this: +Huma does not provide any built-in access token issuing functionality. Instead, you can use any existing library or service to issue tokens. For simplicity's sake, we will assume you are using a third-party service for managing users and issuing tokens, like [Auth0](https://auth0.com/) or [Okta](https://www.okta.com/). A simplified flow chart for OAuth2.0 authorization looks something like this: ```mermaid graph LR From 1e62a9ea7ac86e80e55d145e135b045ad0576cd8 Mon Sep 17 00:00:00 2001 From: "Daniel G. Taylor" Date: Tue, 16 Jan 2024 20:38:58 -0800 Subject: [PATCH 10/11] Update docs/docs/how-to/oauth2-jwt.md Co-authored-by: Stefan Moser <6841360+sm3142@users.noreply.github.com> --- docs/docs/how-to/oauth2-jwt.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/how-to/oauth2-jwt.md b/docs/docs/how-to/oauth2-jwt.md index 341eedf6..b714c59f 100644 --- a/docs/docs/how-to/oauth2-jwt.md +++ b/docs/docs/how-to/oauth2-jwt.md @@ -87,7 +87,7 @@ huma.Register(api, huma.Operation{ ``` > [!WARNING] -> So far, the code above is only documenting the authorization scheme and required scopes, but does not actually authorize incoming requests. The next section will explain to achieve the latter. +> So far, the code above is only documenting the authorization scheme and required scopes, but does not actually authorize incoming requests. The next section will explain how to achieve the latter. ## Authorize Incoming Requests From 498ab8ada59a7f4e924547a432723a5ef1e9146d Mon Sep 17 00:00:00 2001 From: "Daniel G. Taylor" Date: Tue, 16 Jan 2024 20:56:27 -0800 Subject: [PATCH 11/11] fix: apply suggestions, fix formatting --- docs/docs/how-to/oauth2-jwt.md | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/docs/docs/how-to/oauth2-jwt.md b/docs/docs/how-to/oauth2-jwt.md index b714c59f..c996b839 100644 --- a/docs/docs/how-to/oauth2-jwt.md +++ b/docs/docs/how-to/oauth2-jwt.md @@ -25,6 +25,7 @@ graph LR API -->|4. Verify access token & roles| Validate Validate -->|5. Accept/reject| API API --->|6. Success| Handler +``` The access token may be issued in different flavors & formats, but for the remainder of this document we will assume they are [JWTs](https://jwt.io/). @@ -86,8 +87,9 @@ huma.Register(api, huma.Operation{ }) ``` -> [!WARNING] -> So far, the code above is only documenting the authorization scheme and required scopes, but does not actually authorize incoming requests. The next section will explain how to achieve the latter. +!!! Warning + + So far, the code above is only documenting the authorization scheme and required scopes, but does not actually authorize incoming requests. The next section will explain how to achieve the latter. ## Authorize Incoming Requests @@ -149,11 +151,11 @@ func NewAuthMiddleware(jwksURL string) { keySet := NewJWKSet(jwksURL) return func(ctx huma.Context, next func(huma.Context)) { - var anyOfNeededRoles []string + var anyOfNeededScopes []string isAuthorizationRequired := false for _, opScheme := range ctx.Operation().Security { var ok bool - if anyOfNeededRoles, ok = opScheme["myAuth"]; ok { + if anyOfNeededScopes, ok = opScheme["myAuth"]; ok { isAuthorizationRequired = true break } @@ -165,6 +167,10 @@ func NewAuthMiddleware(jwksURL string) { } token := strings.TrimPrefix(ctx.Header("Authorization"), "Bearer ") + if len(token) == 0 { + huma.WriteErr(ctx, http.StatusUnauthorized, "Unauthorized") + return + } // Parse and validate the JWT. parsed, err := jwt.ParseString(token, @@ -173,17 +179,21 @@ func NewAuthMiddleware(jwksURL string) { jwt.WithIssuer("my-issuer"), jwt.WithAudience("my-audience"), ) + if err != nil { + huma.WriteErr(ctx, http.StatusUnauthorized, "Unauthorized") + return + } // Ensure the claims required for this operation are present. scopes = parsed.Get("scopes").([]string) for _ scope := range scopes { - if slices.Contains(anyOfNeededRoles, scope) { + if slices.Contains(anyOfNeededScopes, scope) { next(ctx) return } } - huma.WriteErr(ctx, http.StatusUnauthorized, "Unauthorized") + huma.WriteErr(ctx, http.StatusForbidden, "Forbidden") } } ``` @@ -194,6 +204,10 @@ Lastly, when configuring your API, be sure to include this middleware: api.UseMiddleware(NewAuthMiddleware("https://example.com/.well-known/jwks.json")) ``` +### Supporting different Token Formats + +As mentioned previously, the Oauth2.0 standard does not specify the format of the access token - it merely defines how to get one. Although JWT is a very popular format, a given OAuth2.0 service or library may issue access token in different formats. The gist of what is outlined above should be adaptable to support such tokens as well, but will obviously require different methods for validation and information extraction. In the case of opaque tokens, additional interaction with an IAM server may be required inside the middleware, e.g. calling an introspection endpoint. + ## Optional: Client Auto-Configuration Some clients like [Restish](https://rest.sh/) support [OpenAPI-based auth auto-configuration](https://rest.sh/#/openapi?id=autoconfiguration). This means that you can configure your client to fetch the OpenAPI document and automatically configure itself to use the correct auth mechanism. This is done by adding the `x-cli-config` extension to the OpenAPI: