Customize Security & Authentication

Authentication Overview

Speakeasy-created SDKs have authentication configured automatically based on the securitySchemes defined in your OpenAPI specification.

For APIs authenticated by long-lived tokens (API keys), these should work out of the box. For APIs authenticated by short-lived tokens (OAuth), extra configuration is required to minimize your users' effort.

Support for securitySchemes

In your OpenAPI specification, define the securitySchemes object according to your API's authentication mechanism.

Authentication MechanismLanguage Support
HTTP Basic Authentication✅ All Languages
API Key [header,query]✅ All Languages
Bearer Token Authentication✅ All Languages
OAuth Client Credentials Flow [client_secret_post]✅ Go, Typescript, Python, C# 🏗️ Others
OAuth Client Credentials Flow [others]🏗️ Partial
OAuth Implicit Flow🏗️ Partial
OAuth Refresh Token Flow🏗️ Partial
mTLS🏗️ Partial

HTTP Basic Authentication

Supported in all languages. Defining type: http & scheme: basic will generate authentication that will prompt users for a username and password when instantiating the SDK. The SDK will handle the encoding of the username and password into a base64 string and pass it in the Authorization header.


paths:
/drinks:
get:
operationId: listDrinks
summary: Get a list of drinks.
description: Get a list of drinks, if authenticated this will include stock levels and product codes otherwise it will only include public information.
tags:
- drinks
components:
securitySchemes:
auth:
type: http
scheme: basic
security:
- auth: []


import { SDK } from "speakeasy";
async function run() {
const sdk = new SDK({
security: {
username: "<YOUR_USERNAME_HERE>",
password: "<YOUR_PASSWORD_HERE>",
},
});
const result = await sdk.drinks.listDrinks();
// Handle the result
console.log(result);
}
run();

API Key Authentication

Supported in all languages. Defining type: apiKey & in: [header,query] will generate authentication that will prompt users for a key when instantiating the SDK. The SDK will pass the key either in a header or a query parameter depending on the in field and using the name field as the header/key name.


paths:
/drinks:
get:
operationId: listDrinks
summary: Get a list of drinks.
description: Get a list of drinks, if authenticated this will include stock levels and product codes otherwise it will only include public information.
tags:
- drinks
responses:
"200":
description: OK
#...
components:
securitySchemes:
api_key:
type: apiKey
name: api_key
in: header
security:
- api_key: []


import { SDK } from "speakeasy";
async function run() {
const sdk = new SDK({
apiKey: "<YOUR_API_KEY_HERE>",
});
const result = await sdk.drinks.listDrinks();
// Handle the result
console.log(result);
}
run();

Bearer Token Authentication

Supported in all languages. Defining type: http & scheme: bearer will generate authentication that will prompt users for a token when instantiating the SDK. The SDK will pass the token in the Authorization header using the Bearer scheme, appending the Bearer prefix to the token if not already present.


paths:
/drinks:
get:
operationId: listDrinks
summary: Get a list of drinks.
description: Get a list of drinks, if authenticated this will include stock levels and product codes otherwise it will only include public information.
tags:
- drinks
components:
securitySchemes:
auth:
type: http
scheme: bearer
security:
- auth: []


import { SDK } from "speakeasy";
async function run() {
const sdk = new SDK({
auth: "<YOUR_BEARER_TOKEN_HERE>",
});
const result = await sdk.drinks.listDrinks();
// Handle the result
console.log(result);
}
run();

OAuth 2.0 Authentication

Client Credentials Flow

OAuth2 defines multiple ways of building a request against the tokenUrl endpoint. These are defined below along with our support for each.

Client Authentication MethodDescriptionSupport
client_secret_postThe secret is provided in the request body as application/x-www-form-urlencoded form data
client_secret_basicThe secret is provided in the Authorization header using the Basic authentication scheme-
others--

Defining type: oauth2 and flows: clientCredentials will prompt users for a client ID and client secret when instantiating the SDK. The client credentials flow is used to obtain an access token for the SDK to use when making requests to the API.


paths:
/drinks:
get:
operationId: listDrinks
summary: Get a list of drinks.
description: Get a list of drinks, if authenticated this will include stock levels and product codes otherwise it will only include public information.
security:
- clientCredentials:
- read:drinks
tags:
- drinks
components:
securitySchemes:
clientCredentials:
type: oauth2
flows:
clientCredentials:
tokenUrl: https://speakeasy.bar/oauth2/token/
scopes: {}
security:
- clientCredentials:
- read:basic

Any scopes defined in the global security definition for the oauth2 scheme will be requested along with any specfied at the operation level from the tokenUrl when the SDK makes requests.

Then to enable the generation of the handling code for the client credentials flow, you need to add the following to your gen.yaml file:


configVersion: 2.0.0
generation:
auth:
OAuth2ClientCredentialsEnabled: true


import { SDK } from "speakeasy";
async function run() {
const sdk = new SDK({
security: {
clientID: "<YOUR_CLIENT_ID_HERE>",
clientSecret: "<YOUR_CLIENT_SECRET_HERE>",
},
});
const result = await sdk.drinks.listDrinks();
// Handle the result
console.log(result);
}
run();

Scoping Authentication

Global

Global security allows your users to configure the SDK once and reuse the security configuration for all subsequent calls.

To use global security, define your security configuration in the security block at the root of the SDK.


paths:
/drinks:
get:
operationId: listDrinks
summary: Get a list of drinks.
description: Get a list of drinks, if authenticated this will include stock levels and product codes otherwise it will only include public information.
tags:
- drinks
components:
securitySchemes:
api_key:
type: apiKey
name: api_key
in: header
security: # Here
- api_key: []

In the resulting SDK, the user will be able to define the security configuration in the SDK's instantiation. It will then be automatically applied to all subsequent method calls without needing to be passed in as an argument:


import { SDK } from "speakeasy";
async function run() {
const sdk = new SDK({
apiKey: "<YOUR_API_KEY_HERE>",
});
const result = await sdk.drinks.listDrinks();
// Handle the result
console.log(result);
}
run();

Per-Operation

Info Icon

NOTE

Security Hoisting: In cases where global security is not defined, we will automatically hoist the most commonly occurring operation-level security to be considered global. This will simplify the usage of your SDK.

Operation-specific security configuration allows for overriding one endpoint's authentication configuration.

This is most often used for operations that do not require authentication, or when an operation is part of an authentication flow (for example, invoked to retrieve a shorter-lived access token).

To use operation-specific security, define security within an operation's scope.


paths:
/drinks:
get:
operationId: listDrinks
summary: Get a list of drinks.
description: Get a list of drinks, if authenticated this will include stock levels and product codes otherwise it will only include public information.
security: # Here
- apiKey: []
tags:
- drinks
components:
securitySchemes:
api_key:
type: apiKey
name: api_key
in: header
security:
- {}

In the SDK, the user will be able to pass in a specific security configuration as an argument to the method call:


import { SDK } from "speakeasy";
async function run() {
const sdk = new SDK();
const operationSecurity = "<YOUR_API_KEY_HERE>";
const result = await sdk.drinks.listDrinks(operationSecurity);
// Handle the result
console.log(result);
}
run();

Security Callbacks

Instead of providing credentials once during SDK instantiation, passing a custom authentication function allows your end-user to manage secrets dynamically. For example this can be leveraged to automatically refresh tokens or pull secrets from a secret store. Speakeasy supports security callbacks in most popular languages. Support for additional languages is available as each language becomes GA.

TypescriptPythonGoC#JavaPHPSwiftRuby
✅️🏗️🏗️🏗️

Bearer Authentication

In this example Bearer Authentication is used as the only security scheme:


security:
- bearerAuth: []
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer

The callback function that you pass when initializing the SDK acts as a security source. This function will be called every time a request is made so that tokens can be refreshed if needed.


import { SDK } from "<packageName>";
import { Security } from "<packageName>/models";
const sdk = new SDK({
security: async (): Promise<Security> => {
// refresh token here
const token = "<YOUR_JWT>";
return { bearerAuth: token };
},
});

Custom refresh token flow

Enabling custom OAuth refresh token security in your SDK is also made possible by implementing security callbacks along with some additional configuration outside of your OpenAPI spec.

Step 1: Define OAuth Security in Your OpenAPI Spec

Our current OAuth support does not use the OpenAPI description, but native support is on the way, so don't skip this step!


/oauth2/token:
get:
operationId: auth
security:
- []
responses:
200:
description: OK
content:
application/json:
schema:
type: object
properties:
access_token: string
required:
- access_token
/example:
get:
operationId: example
responses:
200:
description: OK
components:
securitySchemes:
auth:
type: oauth2
flows:
clientCredentials:
tokenUrl: https://speakeasy.bar/oauth2/token/
scopes: {}
security:
- auth: []

Step 2: Add Your Callback Function to Your SDKs

To implement OAuth authentication, you need to write a callback function to handle your OAuth token exchange. To do so, add a file to your SDKs src folder called oauth.ts (or oauth.py for Python, oauth.go for Go, and so on):


import { SDK_METADATA } from "./lib/config";
import * as z from "zod";
// TypeScript SDKs use Zod for runtime data validation. We can use this library
// to describe the shape of the response from the OAuth token endpoint. If the
// response is valid, we can safely access the token and its expiration time.
const tokenResponseSchema = z.object({
access_token: z.string(),
expires_in: z.number().positive(),
});
// This is a rough value that adjusts when we consider an access token to be
// expired. It is used to account for clock drift between the client and server
// and slow/unreliable network.
const tolerance = 5 * 60 * 1000;
/**
* A callback function that can be used to obtain an OAuth access token for use
* with SDKs that require OAuth security. A new token is requested from the
* OAuth provider when the current token has expired.
*/
export function withAuthorization(
clientID: string,
clientSecret: string,
options: { tokenStore?: TokenStore; url?: string } = {}
) {
const {
tokenStore = new InMemoryTokenStore(),
// Replace this with your default OAuth provider's access token endpoint.
url = "https://oauth.example.com/token",
} = options;
return async (): Promise<string> => {
const session = await tokenStore.get();
// Return the current token if it has not expired yet.
if (session && session.expires > Date.now()) {
return session.token;
}
try {
const response = await fetch(url, {
method: "POST",
headers: {
"content-type": "application/x-www-form-urlencoded",
// Include the SDK's user agent in the request so requests can be
// tracked using observability infrastructure.
"user-agent": SDK_METADATA.userAgent,
},
body: new URLSearchParams({
client_id: clientID,
client_secret: clientSecret,
grant_type: "client_credentials",
}),
});
if (!response.ok) {
throw new Error("Unexpected status code: " + response.status);
}
const json = await response.json();
const data = tokenResponseSchema.parse(json);
await tokenStore.set(
data.access_token,
Date.now() + data.expires_in * 1000 - tolerance
);
return data.access_token;
} catch (error) {
throw new Error("Failed to obtain OAuth token: " + error);
}
};
}
/**
* A TokenStore is used to save and retrieve OAuth tokens for use across SDK
* method calls. This interface can be implemented to store tokens in memory,
* a shared cache like Redis or a database table.
*/
export interface TokenStore {
get(): Promise<{ token: string; expires: number } | undefined>;
set(token: string, expires: number): Promise<void>;
}
/**
* InMemoryTokenStore holds OAuth access tokens in memory for use by SDKs and
* methods that require OAuth security.
*/
export class InMemoryTokenStore implements TokenStore {
private token = "";
private expires = Date.now();
constructor() {}
async get() {
return { token: this.token, expires: this.expires };
}
async set(token: string, expires: number) {
this.token = token;
this.expires = expires;
}
}

Step 3: Pass Callback Function in SDK Instantiation

Edit your Readme to instruct users to pass in the callback function when instantiating the SDK. We recommend adding a custom OAuth section that shows users how to instantiate their SDK via the callback function:


import { SDK } from "speakeasy";
const sdk = new SDK({
security: withAuthorization("client_id", "client_secret"),
});
await s.drinks.listDrinks();