# Custom Security Schemes

Custom Security Schemes define a JSON Schema for SDK security options. Combined with [SDK Hooks](/docs/customize/code/sdk-hooks), custom authentication and authorization schemes can be implemented beyond OpenAPI's capabilities.

<Callout title="Availability" type="info">
  Custom Security Schemes are only available for [Business and Enterprise users](/pricing).

</Callout>

### Language support

### Define a custom security scheme

Define the custom security scheme under `components -> securitySchemes` and reference it in the `security` section. Set the `type` to `http` and the `scheme` to `custom`. Use the `x-speakeasy-custom-security-scheme` extension to specify a JSON Schema. This schema must include at least one property and can accommodate multiple properties with different schema definitions.

```yaml
openapi: 3.1.0
info:
  title: Custom Security Scheme Example
  version: 1.0.0
security:
  - myCustomScheme: [] # reference to the custom security scheme defined below
# ...
components:
  securitySchemes:
    myCustomScheme: # defined as usual under components -> securitySchemes
      type: http
      scheme: custom # type: http, scheme: custom is used to identify the custom security scheme
      x-speakeasy-custom-security-scheme: # A JSON Schema is then provided via the x-speakeasy-custom-security-scheme extension
        schema:
          type: object # the JSON Schema MUST be defined as an object with at least one property, but can then have any number of properties with any schema
          properties:
            appId:
              type: string
              example: app-speakeasy-123
            secret:
              type: string
              example: MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI
          required:
            - appId
            - secret
```

### Initialize the custom security scheme

Once the SDK is regenerated, the custom security scheme can be configured globally or per operation, depending on the `security` definitions.

<CodeWithTabs client:visible
  tabs={[
    {
      label: "TypeScript",
      language: "typescript",
      code: `import { SDK } from "openapi";

  const sdk = new SDK({
    security: {
      appId: "app-speakeasy-123",
      secret: "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI",
    },
  });`,
    },
    {
      label: "Python",
      language: "python",
      code: `import openapi
  from openapi import SDK

  s = SDK(
    security=openapi.Security(
        app_id="app-speakeasy-123",
        secret="MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI",
    ),
  )`,
    },
    {
      label: "Go",
      language: "go",
        code: `package main

  using Openapi.Models.Components;
  var sdk = new SDK(securitySource: () => new Security() {
      AppId = "app-speakeasy-123",
      Secret = "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI"
  });`,
    },
    {
      label: "Java",
      language: "java",
        code: `import org.openapis.openapi.SDK;

  SDK sdk = SDK.builder()
          .security(Security.builder()
              .appId("app-speakeasy-123")
              .secret("MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI")
              .build())
          .build();`,
    },
    {
      label: "PHP",
      language: "php",
      code: `use Openapi;
  use Openapi\\Models\\Components;
  $sdk = Openapi\\SDK::builder()
      ->setSecurity(
          new Components\\Security(
              appId: 'app-speakeasy-123',
              secret: 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI',
          )
      )->build();`,
    },
    {
      label: "Ruby",
      language: "ruby",
      code: `require 'openapi'
  $sdk = Openapi\\SDK::builder()
      ->setSecurity(
          new Components\\Security(
              appId: 'app-speakeasy-123',
              secret: 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI',
          )
      )->build();`,
}
]}
/>

### Use the custom security scheme

Use [SDK Hooks](/docs/customize/code/sdk-hooks) to access user-provided security values and enable custom authentication workflows, like adding headers to requests.

The following example illustrates accessing a custom security scheme in a hook and adding headers to a request:

<CodeWithTabs client:visible
  tabs={[
    {
      label: "TypeScript",
      language: "typescript",
      code: `import { Security$outboundSchema } from "../models/components/security.js";

      if (typeof sec === "function") {
        sec = sec();
      }
      if (!sec) {
        throw new Error("security source is not defined");
      }

      // Use the Zod schema to parse the security object
      const customSec = Security$outboundSchema.parse(sec);

      // Access the values from the parsed object and add them to the request headers
      request.headers.set("X-Security-App-Id", customSec.appId);
      request.headers.set("X-Security-Secret", customSec.secret);

      return request;
    }
  }`,
    },
    {
      label: "Python",
      language: "python",
      code: `import requests
  from openapi import SDK
  from typing import Union
  from .types import BeforeRequestContext, BeforeRequestHook

  class CustomSecurityHook(BeforeRequestHook):
      def before_request(self, hook_ctx: BeforeRequestContext, request: requests.PreparedRequest) -> Union[requests.PreparedRequest, Exception]:
          security = hook_ctx.security_source
          if not security.app_id or not security.secret:
              raise ValueError("Missing security credentials")
          # Add security headers to the request
          request.headers["X-Security-App-Id"] = security.app_id
          request.headers["X-Security-Secret"] = security.secret
          return request`,
    },
    {
      label: "Go",
      language: "go",
      code: `package hooks

      using System.Net.Http;
      using System.Net.Http.Headers;
      using System.Threading.Tasks;
      using Openapi.Models.Shared;
      public class CustomSecurityHook : IBeforeRequestHook
      {
          public async Task<HttpRequestMessage> BeforeRequestAsync(BeforeRequestContext hookCtx, HttpRequestMessage request)
          {
              await Task.CompletedTask;
              if (hookCtx.SecuritySource == null)
              {
                  throw new Exception("Security source is null");
              }
              Security? security = hookCtx.SecuritySource() as Security;
              if (security == null)
              {
                  throw new Exception("Unexpected security type");
              }
              // Add security values to the request headers
              request.Headers.Add("X-Security-App-Id", security.AppId);
              request.Headers.Add("X-Security-Secret", security.Secret);
              return request;
          }
      }
  }`,
    },
    {
      label: "Java",
      language: "java",
      code: `import java.net.http.HttpRequest;

  // an instance of this class is registered in SDKHooks
  public class CustomSecurityHook implements BeforeRequest {

      @Override
      public HttpRequest beforeRequest(BeforeRequestContext context, HttpRequest request) throws Exception {
          if (!context.securitySource().isPresent()) {
              throw new IllegalArgumentException("security source is not present");
          }
          // this example is for global security, cast to the appropriate HasSecurity
          // implementation for method-level security
          Security sec = (Security) context.securitySource().get().getSecurity();
          return Helpers.copy(request) //
                  .header("X-Security-App-Id", sec.appId()) //
                  .header("X-Security-Secret", sec.secret()) //
                  .build();
      }
  }`,
    },
    {
      label: "PHP",
      language: "php",
        code: `namespace OpenAPI\\OpenAPI\\Hooks;
  use OpenAPI\\OpenAPI\\Models\\Shared\\Security;
  use Psr\\Http\\Message\\RequestInterface;
  class CustomSecurityHook implements BeforeRequestHook
  {
      public function beforeRequest(BeforeRequestContext $context, RequestInterface $request): RequestInterface
      {
          if ($context->securitySource === null) {
              throw new \\InvalidArgumentException('Security source is null');
          }
          $security = $context->securitySource->call($context);
          if (! $security instanceof Security) {
              throw new \\InvalidArgumentException('Unexpected security type');
          }
          $request = $request->withHeader('Idempotency-Key', 'some-key');
          // Add security values to the request headers
          $request = $request->withAddedHeader('X-Security-App-Id', $security->appId);
          $request = $request->withAddedHeader('X-Security-Secret', $security->secret);
          return $request;
      }
  }`,
    },
    {
      label: "Ruby",
      language: "ruby",
      code: `require_relative './types'
  require 'sorbet-runtime'
  module OpenApiSDK
    module SDKHooks
      class CustomSecurityHook
        extend T::Sig
        include AbstractBeforeRequestHook
        sig do
          override.params(
            hook_ctx: BeforeRequestHookContext,
            request: Faraday::Request
          ).returns(Faraday::Request)
        end
        def before_request(hook_ctx:, request:)
          raise ArgumentError, 'Security source is null' if hook_ctx.security_source.nil?
          security = hook_ctx.security_source.call
          raise ArgumentError, 'Unexpected security type' unless security.is_a?(Models::Shared::Security)
          # Add security values to the request headers
          request.headers['X-Security-App-Id'] = security.app_id
          request.headers['X-Security-Secret'] = security.secret
          request
        end
      end
    end
  end`,
}
]}
/>
