Applications that provide first class APIs require more than simple authentication, they require api keys. API keys generated by your users allow their programmatic services to interact with your apis. These must be generated in a safe way and have different requirements than UI tokens.
In most cases with authentication, you’ll have JWTs to identify and authorize a user. These JWTs are coming from your authentication provider. Internally, you may also have api keys to authenticate your internal services from one to another. However having to authenticate users and service client differently causes issues:
- Opaque api keys have no expiration date, so exposing them requires immediate rotation
- API keys do not contain identifying information to provide identity of the service client, making it impossible to know who the caller is supposed to be or which tenant or account should be accessed.
- Application services are required to understand multiple authentication strategies which becomes more problematic, when there are multiple services, because then each needs to understand how to validate the token and share the database of public keys.
- Frequently keys are incorrectly stored in plaintext when they should hashed and salted just like a password adding complexity to key generation.
- API keys are incorrectly coupled to the user that created them instead of innately shareable. Many consumers of APIs want to uniquely identify their service clients rather than couple them to their own user identity. (Family accounts and B2B applications).
The best way to do this is to provide service clients a way to create JWTs and more specifically be an identity provider. By converting service client api keys to public/private key format and signing a JWT, application APIs can verify tokens in a consistent manner. Also, since service clients are using JWTs they will contain identifying information. Additionally, the api key will never be exposed outside the service so security is increased.
- The first step is generate a public/private key pair, store the public key in your database, and distribute the private key. Along with the private key provider client identification information that you expect the service client to populate.
- The service client uses the private key to create a JWT that can be passed back to API and can be verified against the public key JWK that was created in the first step.
- The application API then verifies the JWT token using the public key and authorizes the caller using the token to perform the action.
- [Later] When the access token expires the client can generate a new JWT.
This avoids all the issues that are generated by using the common flat api key as well as increases security for both the application API as well as the client caller.
Creating the key pair is simply done using available cryptography libraries in your preferred language. How there is more complexity added to make sure the configuration of your JWKs and identifying information is correctly consumed.
Getting this all correct can be challenging and leaves possible attack surfaces for vulnerabilities. To avoid these issues there is the service client API functionality available in the Authorization API.
The manual steps for doing the same thing are available in the UI:
And then add an access key:
So far we’ve solved the application side, but the service client still needs to connect. Pulling apart the access key and generating the client JWT is not simple, but the functionality is already provided in the authress-sdk.js ( all available sdks).
We can wrap the service token provider functionality and deliver an application specific SDK that uses the builtin to handle all the complexity.
Originally published at https://authress.io on January 1, 2021.