Skip to main content

4 posts tagged with "OpenAPI"

View All Tags
← Back to the liblab blog

Do you wish to increase the engagement of users with your API? Or are you perhaps afraid that your users will stop using your API because it’s too boring? Too... simple? Too user-friendly?

Well, prepare yourself for a paradigm shift. In this blog post, we will explore how to utilize some well known programming techniques and showcase our skills to make your OpenAPI Spec more exciting.

Tip 1: Ensure your paths and operationIds are unique

It is clear that paths and operationIds must be unique - at all costs.

Luckily, developers have already come up with genius techniques of ensuring uniqueness so it only remains on us to employ these techniques. For paths we can use an auto-increment integer. This means that our first path should be /1, second /2 and so on.

💡 Pro tip: You might be tempted to track the last number used, but isn’t it more exciting for your users to wonder what happened to missing path /5?

And to show how much we know about programming we will use another technique - UUIDs - for operationIds. UUIDs might be long, barely readable strings of characters that are incomprehensible to humans, but what is truly more important - uniqueness or understandability? Although they are very complex, they ensure that no two operationIds are ever the same, thus eliminating any possibility of confusion.

paths:
# Good
/1:
get:
operationId: 018e3981-3250-701c-b27b-1f5db826f2b0
# Bad - possible collisions
/pets:
get:
operationId: listPets

Tip 2: Use consistent status codes

You’ve heard it before - being consistent goes a long way. The hardest choice is what this consistency should be. We should be grateful that this is a very simple choice when it comes to status codes.

In this stressful modern day, nobody will like you if you’re too intense. Instead, relax! Cool people are always OK with whatever happens. Why should your API be any different?

That’s why you should return a 200 status code no matter what happens.

There has been an unexpected error? Don’t panic, say OK. After all, is there any better feeling than when there is something obviously wrong, but when your ask your partner, they say everything is OK?

responses:
- 200:
content:
application/json:
schema:
type: string
enum:
- OK

Tip 3: Increase engagement and security through obscurity

Want to increase the engagement with your API and increase security at the same time in just one simple step?

Why reveal what an endpoint does? If this remains hidden - malicious users can’t harm you - you can’t hack what you don’t know. What data does it return, you ask? Who knows? Who cares? That's the charm of it. The vaguer your descriptions, the more precious hours your fellow developers will spend learning your API - and isn't that what the spirit of programming is all about? They will try out many endpoints in attempt to find what they need instead of calling only the one they need.

Engagement with your API will skyrocket 🚀

# Good
description: Returns data
# Bad - too revealing
description: Gets a Pet by ID

Tip 4: Use query parameters

In the intricate dance of confusion that is your Open API, one must not forget the elusive query parameters. The key here is secrecy. Never, under any circumstances whatsoever, should you reveal your query parameters. Let them be like a hidden treasure, waiting for the bravest of developers to uncover. How will they discover them? Well brute force, of course! They can try any and every possible query parameter, until they find what they are looking for. The fact that there are query parameters is all the information they need. What are these parameters? What is their purpose? These are mysteries best left unsolved. After all, life is more exciting when it's full of surprises.

parameters:
- name: "?"
in: query

Tip 5: Keep documentation to a minimum

If you want to allow only the most worthy developers to use your API - follow this tip.

Documentation, the ultimate spoon-feeding tool for developers. To truly test their mettle, provide the bare minimum. The less guidance they have, the more they'll learn - or so you can tell yourself as you watch them grow in order to navigate your API.

After all, why would they waste countless hours reading documentation - when they can immediately just start using your API? Don’t obstruct them in this quest - you are either with them or against them.

Here's an example of how to keep your documentation to a minimum:

Tying it all together

So we’ve seen some very good suggestions, but how does it all work together?

Best if we see it in example:

paths:
/1:
get:
operationId: 018e3981-3250-701c-b27b-1f5db826f2b0
description: Returns data
responses:
- 200:
content:
application/json:
schema:
type: string
enum:
- OK
parameters:
- name: "?"
in: query
...

Conclusion

In conclusion, a truly exceptional Open API spec is not one that is easy to understand and use, but one that leaves developers scratching their heads, questioning their decision to be a programmer.

Follow these tips, and you too can ascend to the ranks of the masters of the mysterious, the ambiguous, the truly incomprehensible Open API. Remember, confusion is the path to enlightenment... or at least, that's what I whisper to myself as I debug my code at the ungodly hour of 3am.

https://www.notion.so/icons/theatre_lightgray.svg Disclaimer: this post was reviewed and approved by the April fools committee.

← Back to the liblab blog

A mix of anticipation and dread washes over me as I open a new inbound email with an attached specification file. With a heavy sigh, I begin scrolling through its contents, only to be greeted by disappointment yet again.

The API request bodies in this specification file suffer from a lack of essential details, specifically the absence of the actual properties of the HTTP call. This makes it difficult to determine the expectations and behavior of the API. Not only will API consumers have a hard time understanding the API, but the lack of properties also hinders the use of external libraries for validation, analysis, or auto-generation of output (e.g., API mocking, testing, or liblab's auto SDK generation).

After encountering hundreds of specification files (referred to as specs) in my role at liblab, I’ve come to the conclusion that most spec files are in varying degrees of incompletion. Some completely disregard the community standard and omit crucial information while others could use some tweaking and refinement. This has inspired me to write this blog post with the goal of enhancing the quality of your spec files. It just so happens that this goal also aligns with making my job easier.

In the upcoming sections, we'll go over three common issues that make your OpenAPI spec fall short and examine possible solutions for them. By the end of this post you’ll be able to elevate your OpenAPI spec, making it more user-friendly for API consumers, including developers, QA engineers, and other stakeholders.

Three Reasons Why Your OpenAPI Spec Sucks

You’re Still Using Swagger

Look, I get it. A lot of us still get confused about the differences between Swagger and OpenAPI. To make things simple you can think of Swagger as the former name of OpenAPI. Many tools are still using the word "Swagger" in their names but this is primarily due to the strong association and recognition that the term Swagger had gained within the developer community.

If your “Swagger” spec is actually an OpenAPI spec (indicated by the presence of "openapi: 3.x.x" at the beginning), all you need to do is update your terminology.

If you’re actually using a Swagger spec (a file that begins with "swagger: 2.0”), it's time to consider an upgrade. Swagger has certain limitations compared to OpenAPI 3, and as newer versions of OpenAPI are released, transitioning will become increasingly challenging.

Notable differences:

  • OpenAPI 3 has support for oneOf and anyOf that Swagger does not provide. Let us look at this example:
openapi: 3.0.0
info:
title: Payment API
version: 1.0.0
paths:
/payments:
post:
summary: Create a payment
requestBody:
required: true
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/CreditCardPayment"
- $ref: "#/components/schemas/OnlinePayment"
- $ref: "#/components/schemas/CryptoPayment"
responses:
"201":
description: Created
"400":
description: Bad Request

In OpenAPI 3, you can explicitly define that the requestBody for a /payments POST call can be one of three options: CreditCardPayment, OnlinePayment, or CryptoPayment. However, in Swagger you would need to create a workaround by adding an object with optional fields for each payment type:

swagger: "2.0"
info:
title: Payment API
version: 1.0.0
paths:
/payments:
post:
summary: Create a payment
consumes:
- application/json
produces:
- application/json
parameters:
- name: body
in: body
required: true
schema:
$ref: "#/definitions/Payment"
responses:
"201":
description: Created
"400":
description: Bad Request

definitions:
Payment:
type: object
properties:
creditCardPayment:
$ref: "#/definitions/CreditCardPayment"
onlinePayment:
$ref: "#/definitions/OnlinePayment"
cryptoPayment:
$ref: "#/definitions/CryptoPayment"
# Make the properties optional
required: []

CreditCardPayment:
type: object
# Properties specific to CreditCardPayment

OnlinePayment:
type: object
# Properties specific to OnlinePayment

CryptoPayment:
type: object
# Properties specific to CryptoPayment

This example does not resemble the OpenAPI 3 implementation fully as the API consumer has to specify the type they are sending through a property field, and they also might send more than of the fields since they are all marked optional. This approach lacks the explicit validation and semantics provided by the oneOf keyword in OpenAPI 3.

  • In OpenAPI you can describe multiple server URLs while in Swagger you’re bound to only one:
{
"swagger": "2.0",
"info": {
"title": "Sample API",
"version": "1.0.0"
},
"host": "api.example.com",
"basePath": "/v1",
...
}
openapi: 3.0.0
info:
title: Sample API
version: 1.0.0
servers:
- url: http://api.example.com/v1
description: Production Server
- url: https://sandbox.api.example.com/v1
description: Sandbox Server
...

You’re Not Using Components

One way of making an OpenAPI spec more readable is by removing any unnecessary duplication — the same way as a programmer would with their code. If you find that your OpenAPI spec is too messy and hard to read you might be under-utilizing the components section. Components provide a powerful mechanism for defining reusable schemas, parameters, responses, and other elements within your specification.

Let's take a look at the following example that does not utilize components:

openapi: 3.0.0
info:
title: Nested Query Example
version: 1.0.0
paths:
/users:
get:
summary: Get users with nested query parameters
parameters:
- name: filter
in: query
schema:
type: object
properties:
name:
type: string
age:
type: number
address:
type: object
properties:
city:
type: string
state:
type: string
country:
type: string
zipcode:
type: string
...
/user/{id}/friend:
get:
summary: Get a user's friend
parameters:
- name: id
in: path
schema:
type: string
- name: filter
in: query
schema:
type: object
properties:
name:
type: string
age:
type: number
address:
type: object
properties:
city:
type: string
state:
type: string
country:
type: string
zipcode:
type: string
...

The filter parameter in this example is heavily nested and can be challenging to follow. It is also used in its full length by two different endpoints. We can consolidate this behavior by leveraging component schemas:

openapi: 3.0.0
info:
title: Nested Query Example with Schema References
version: 1.0.0
paths:
/users:
get:
summary: Get users with nested query parameters
parameters:
- name: filter
in: query
schema:
$ref: "#/components/schemas/UserFilter"
...
/user/{id}/friend:
get:
summary: Get a user's friend
parameters:
- name: id
in: path
schema:
type: string
- name: filter
in: query
schema:
$ref: "#/components/schemas/UserFilter"
...
components:
schemas:
UserFilter:
type: object
properties:
name:
type: string
age:
type: number
address:
$ref: "#/components/schemas/AddressFilter"

AddressFilter:
type: object
properties:
city:
type: string
state:
type: string
country:
type: string
zipcode:
type: string

The second example is clean and readable. By creating UserFilter and AddressFilter we can reuse those schemas throughout the spec file, and if they ever change we will only have to update them in one place.

You’re Not Using Descriptions, Examples, Formats, or Patterns

You finally finished porting all your endpoints and models into your OpenAPI spec. It took you a while, but now you can finally share it with development teams, QA teams, and even customers. Shortly after you share your spec with the world, the questions start arriving: “What does this endpoint do? What’s the purpose of this parameter? When should the parameter be used?”

Lets take a look at this example:

openapi: 3.0.0
info:
title: Sample API
version: 1.0.0
paths:
/data:
post:
summary: Upload user data
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
age:
type: integer
email:
type: string
responses:
"200":
description: Successful response

We can deduce from it that data needs to be uploaded, but questions remain: What specific data should be uploaded? Is it the data pertaining to the current user? Whose name, age, and email do these attributes correspond to?

openapi: 3.0.0
info:
title: Sample API
version: 1.0.0
paths:
/data:
post:
summary: Upload user data
description: >
Endpoint for uploading new user data to the system.
This data will be used for personalized recommendations and analysis.
Ensure the data is in a valid JSON format.
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: The name of a new user.
age:
type: integer
description: The age of a new user.
email:
type: string
description: The email address of a new user.
responses:
"200":
description: Successful response

You can’t always control how your API was structured, but you can control the descriptions you give it. Reduce the number of questions you receive by adding useful descriptions wherever possible.

Even after incorporating descriptions, you still might be asked about various aspects of your OpenAPI spec. At this point, you might be thinking, "Sharon, you deceived me! I added all those descriptions yet the questions keep on coming.”

Before you give up, have you thought about adding examples?

Lets take a look at this parameter:

parameters:
- name: id
in: path
required: true
schema:
type: string
description: The user id.

Based on the example, we understand that "id" is a string and serves as the user's identifier. However, despite your QA team relying on your OpenAPI spec for their tests, they are encountering issues. They inform you that they are passing a string, yet the API call fails. “That’s because you’re not passing valid ids”, you tell them. You rush to add an example to your OpenAPI spec:

parameters:
- name: id
in: path
required: true
schema:
type: string
example: e4bb1afb-4a4f-4dd6-8be0-e615d233185b
description: The user id.

After your update your spec a follow up question arrives: would "d0656a1f-1lac-4n7b-89de-3e8ic292b2e1” be a good example as well? The answer is no since the characters 'l' and 'n' in the example are not valid hexadecimal characters, making them illegal in the UUID format:

parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
example: e4bb1afb-4a4f-4dd6-8be0-e615d233185b
description: The user id.

Finally your QA team has all the information they need to interact with the endpoints that use this parameter.

But what if a parameter is not of a common format? That’s when regex patterns come in:

parameters:
- name: id
in: path
required: true
schema:
type: string
pattern: "[a-f0-9]{32}"
example: 2675b703b9d4451f8d4861a3eee54449
description: A 32-character unique user ID.

By using the pattern field, you can define custom validation rules for string properties, enabling more precise constraints on the data accepted by your API.

You can read more about formats, examples, and patterns here.

Conclusion

This list of shortcomings is certainly not exhaustive, but the most common and easily fixable ones presented in this post include upgrading from Swagger, utilizing components effectively, and providing comprehensive documentation. By making these improvements, you are laying the foundation for successful API documentation. When working on your spec, put yourself in the shoes of a new API consumer, since this is their initial interaction with the API. Ensure that it is well-documented and easy to comprehend, and set the stage for a positive developer experience.

You can read more OpenAPI tips in some of our other blog posts:

← Back to the liblab blog

An SDK is a set of software development tools that enable developers to create applications for a specific software platform or framework. In the context of APIs, an SDK typically includes libraries, code samples, and documentation that simplify the process of integrating the API into an application.

Offering your developers a good SDK will increase the adoption of your APIs and provide the necessary safeguards for how to access them.

Why is adding an SDK to your API beneficial?

Here are some of the reasons why adding an SDK to your API is beneficial:

Reduce Development Time and Complexity

By providing an SDK, you can significantly reduce the time and effort required to integrate your API into an application. The SDK provides pre-built code libraries that developers can use to quickly and easily interact with your API, reducing the amount of time they need to spend coding and debugging. If you provide your developers only with an API, they will need to define things like auth logic, pagination, error handling, and other complex logic. This can be built-in into your SDK so developers can implement your API within one line of code.

For example, instead of storing pagination tokens and asking your developers to implement their own logic, they can simply use something like mySDK.getData(skip: 10).

Improved Developer Experience

When you provide an SDK, you're not just providing code libraries; you're also providing documentation and code samples that help developers understand how to use your API effectively. This makes it easier for developers to integrate your API into their applications and reduces the likelihood of errors or issues arising during development. IntelliSense and code suggestions will improve the DevEx of your API by giving your developers information about the available calls they can make. Built-in documentation guides developers on what to expect (request/response) in each API call.

SDK Auto completion

Increased Adoption

When you make it easier for developers to use your API, you increase the likelihood that they will actually do so. This can help to drive adoption of your API and increase its overall usage. If you want to increase your API adoption, offering SDKs will help.

Easier Maintenance and Upgrades

When you provide an SDK, you have more control over how developers interact with your API. This makes it easier to make changes or upgrades to your API without breaking existing applications that use it. By providing an SDK, you can ensure that developers are using your API in a consistent and supported way, which can reduce the risk of issues arising.

Better Security

An SDK can also help to improve the security of your API. By providing pre-built code libraries, you can ensure that developers are using secure coding practices when interacting with your API. This reduces the likelihood of security vulnerabilities arising due to coding errors or mistakes. You can also catch API errors in the SDK and have client-side logic to handle it. Retries are an example of how you can throttle the API access in event of errors.

Conclusion

Adding an SDK to your API can provide significant benefits for both developers and API providers. It simplifies the integration process, improves the developer experience, increases adoption, makes maintenance and upgrades easier, and improves security. If you're developing an API, consider providing an SDK to help developers integrate your API more easily and effectively.

Building true idiomatic SDKs in multiple languages takes time and knowledge. Here at liblab, we offer it as a simple, fast straightforward service. Give liblab a try and check how we can automatically build SDKs for your API in seconds, not months.

← Back to the liblab blog

This post will look at what security options OpenAPI gives us, what companies big and small are actually doing, and what you should be doing when you build your own API.

  1. Maybe not Hacker News, but they have a special deal with Firebase to give people unrestricted access to the firehose. I put together a quick Hacker News OpenAPI spec for some testing

Security in the OpenAPI Spec

If you’re like me and you really dig formatting languages, you’re in for a good time because the OpenAPI Specification v3.1.0 is a treat. But if you don’t plan to add this to your nightstand for bed-time reading, this section will walk you through how security is defined.

There are five types of security, and three places to define it.

Five Types of Security in OpenAPI

In a Swagger 2.0 you had three types. OpenAPI dropped one and added three.

The five types in OpenAPI are: apiKey, http, oauth2, mutualTLS, and openIdConnect. The first three are the most commonly seen, and we’ll talk about them a lot in this post. The last two are new, and are rarely seen in the wild.

(The original three types in Swagger 2.0 were basic, apiKey, and oauth2. You can still define a basic authentication with OpenAPI, using type: http; scheme: basic, but that is not recommended for reasons we’ll talk about later.)

Where Security is defined in OpenAPI

There are three places to place security in an API spec.

Under #/components/securitySchemes

This is where you define your security options.

(Simpler APIs will normally only have one option.)

The key name is whatever you want, you’ll use it to reference this object from elsewhere in the spec. The only required parameter is type, which should be one of the five types we talked about above. The other parameters change depending on the type.

Here’s an example of an API that has two security schemes.

components:
securitySchemes:
myBearerAuth:
type: http
scheme: bearer
bearerFormat: jwt
myApiKey:
type: apiKey
name: key_name
in: header

Interesting side note: The securitySchemes in components is a bit of an odd duck. Most of the time, components is for reusable objects. If you find yourself writing an object again and again, you can put it in components, and just reference that component. But with security, the securitySchemes in components is the only place to define security; it has to be defined here.

Under #/security

This is the default security requirement to use for the whole API. This should match up to a named security scheme that was (or will be) defined in #/components/securitySchemes.

If this top level security object is missing, or if it’s an empty object, then this API will have no security by default. You can see this in a lot of smaller APIs that have a few open endpoints for everyone, but then define operation-specific security for the rest.

Here’s an example:

security:
- myApiKey: []

You might be wondering why there’s an empty array here. Good wondering! This array is for scopes, which are strings that let you define fine-grained security permissions. Often it’ll look like “read:foo” and “write:foo”. Oauth2, and it’s newer cousin openIdConnect, always take scopes, other security types may not.

This is a key/value pair, and the value is always an array. If you don’t have any scopes, then you have to have an empty array.

Under a specific operation

Just like above, we use one of the defined security schemes, but this time only for a specific operation on a specific path.

If there is no security defined for an operation, then the API will use the top-level default under #/security.

In this example, we use myBearerAuth for anyone who wants to get /users.

paths:
/users:
get:
security:
- myBearerAuth:
- 'read:users'
- 'public'

Here we’re using scopes. The user of the SDK has to have either a “read:users” or a “public” scope.

And that’s the last we’ll say about scopes.

Security in Practice

In practice, when hitting an endpoint, the security is sent out with the request using one of three methods. One of these is terrible, see if you can spot it.

  • Bearer — A token in the header, which is how OAuth is used. It looks like Authorization: Bearer tokenValue
  • API Key — A key/value pair that could be in the header, query, or even in a cookie, but the best practice is to put it in the header, and often looks like Authorization: apiKey apiKeyValue
  • Basic — A username/password in the header, in the clear. It looks like Authorization: Basic username:password.

Did you spot the terrible method? That’s right, it’s Basic. Don’t send around a username and password with every API call, it’s insecure. We’ll talk about recommendations later.

What API security are big companies using?

The big companies rarely have official and easy-to-find OpenAPI specs, so this information was mostly gleaned from their documentation portals.

Facebook uses OAuth2

Dropbox uses OAuth2

Twitter uses OAuth and OAuth2

Github uses OAuth2, and sometimes Basic Authentication, which is worrisome.

Microsoft Azure uses OAuth2, and recommends building your own apps similarly:

The results are unsurprisingly unanimous: big companies use OAuth2.

What API security are other companies using?

Getting info on lot of other companies is more difficult. Either we have to dive deeply into thousands of API documentation portals, or we can run some simple statistics on a collection of specs. This post will go the latter route.

Methodology

To do this, I’m turning to my favorite JSON Query tool: jq! Although in reality many specs are written in YAML, so we’ll have to turn to its YAML wrapper: yq!

Here’s the basic command.

yq '.components.securitySchemes' openapi.yaml

A good source of a OpenAPI specs is the OpenAPI Directory. It’s a well-maintained resource filled with thousands of specs. We’ll run some statistics on those.

To get some simple stats, I ran a series of UNIX commands on the spec in the OpenAPI Directory. They’re rough, but they work well enough to build our instincts. You can follow along at home.

# total number of files
find . -name "*.yaml" -print | wc -l

3771

# lets save some output, to look at it twice
find . -name "*.yaml" -print0 | /
xargs -0 -n24 -P4 yq '.components.securitySchemes' /
> out 2>/dev/null

# number of APIS without a securityScheme
grep ^null out | wc -l

2452

# ranked list
cat out | grep '"type":' | cut -d\" -f 4 | sort | uniq -c | sort -rn

980 apiKey
853 oauth2
177 http
2 openIdConnect

Now there’s one problem I know of right off the bat: these statistics are overwhelmed by a small number of companies with a lot of specs. Azure alone has 653 specs, each with multiple versions, for a total of 1,832 files in their subsection of this repo. It’s a classic power law distribution.

We’ll run this all again, but this time excluding the five largest offenders, so we only look companies with a smaller number of specs. There is still more cleaning up we could do, but this gets us most of the way to what we want.

(The actual Unix commands for this part are left as an exercise to the reader, mine were pretty messy.)

Here are the new results.

# number of APIS without a securityScheme
499

# ranked list
245 apiKey
123 http
88 oauth2
2 openIdConnect

Conclusions

Now this is interesting, but not too surprising.

OAuth2 is strong choice for authentication, but it’s also a difficult and complex framework; the IETF working group lost a key editor over that fact, and even people who spend a lot of time in the OAuth2 world will sometimes recommend against it. Conversely apiKey can be handled with simple home brewed solutions.

The http type is a more complex question. Everything depends on what comes next, in the scheme parameter. This parameter is required for http types, and has to be a registered scheme.

I did some quick manual checking of APIs with an http type, and I found a fairly even mix of two schemes: ****

  • “scheme: bearer” - This is passing a token in the headers. Often this is defined as a JWT token, which is a very modern and secure method.
  • “scheme: basic” - This is passing the username and password in the headers, and is basically insecure.OpenAPI specs that use this were sometimes using it as an option alongside other security schemes, but it was still unfortunate to see it at all.

Conclusions part 2, when security is missing

I did learn one big thing: at the beginning of this post I said, “Every API has security,” and now I know that many APIs do not, in fact, have security. If I included the “no security” in the pie chart it would have been 52% of the total.

To understand this problem more, I did some spot checks of specs without security.

  • Many were small projects, and they possibly were never meant to be used. I’m suspicious anytime I see a spec that was committed to Github with version 1.0.0 and then never touched again.
  • Some specs were written by enthusiasts for an existing but un-specced API, and it’s possible they just didn’t write a complete spec.
  • I saw one API that did have token-based security, but was only mentioned in the examples.
  • Amazon has their AWS OpenAPI vendor extensions, and specs that use them may not show up in my methodology.
  • There were a few specs that were autogenerated from other places, like protobufs, and it’s likely that something was lost in translation.

So what should you do?

Use OAuth2 if you can, apiKey if you want.

That’s it. That’s the recommendation.

For more details, keep reading. But you can stop here and be happy with what you’ve learned.

apiKey

In spite of having the word API in it, apiKey is technically no longer the best practice for APIs. Phil Karlton said that naming thing is one of the two hardest things in computer science, and this small irony is more proof of that.

But despite what the best practice people say, whoever they are, API keys are still a commonly-used method of security, and perfectly fine for many small APIs.

If you want to keep using your API key security make sure it’s submitted in the headers and not in the query string, that way they’ll be less likely to leak. (If you’ve been using Github API, you may have noticed they deprecated query tokens last year, after a series of deliberate “brown-outs” to get people off their butts.)

But even here there are exceptions. Cisco uses apiKey in a cookie that is obtained from a Single-Sign-On assertion.

Basic

Don’t use basic authentication.

Don’t submit the user’s username and password in the clear.

If you still want to keep using your Basic authentication: don’t.

Even the RFS that defined Basic Authentication says don’t.

The Basic authentication scheme is not a secure method of user authentication… — RFC 2617: HTTP Authentication: Basic and Digest Access Authentication

OAuth2

OAuth2 is where you probably want to be, but it has some gotchas, and its best practices have evolved over time. A few notes to be aware of are:

Now what?

If you’ve come this far, then you may have excitedly built something like this in your OpenAPI spec.

{
"type": "oauth2",
"flows": {
"authorizationCode": {
"authorizationUrl": "https://example.com/api/oauth/dialog",
"tokenUrl": "https://example.com/api/oauth/token",
"scopes": {
"write:pets": "modify pets in your account",
"read:pets": "read your pets"
}
}
}
}

Your customers are happy to finally get some solid security, but then they ask you how to use it in their code. You spent all this time carefully implementing good security in the server, and now you have to implement the other end of it in an SDK?

That is a lot of hard, difficult work.

If only there was a better solution.

Use liblab!

Use liblab!

Give us your API, and we’ll generate SDKs with best practices built in. We won’t even generate an SDK that will put the user at risk, we’ll fail with friendly error messages and recommendations on how to fix it. We’ll make sure your security choices will never become someone else’s postmortem.

We even produce documentation for your API, so your users will know exactly how your security policies work.

liblab!