Sometimes, you may want your services to be able to talk to each other in an authenticated manner, and even perform some authorization. This is not easy to do and you might have scratched your head a bunch about how to do it. In this post I’m going to show you how to do something like this using hashicorp’s Vault. At the end of this post you’ll be able to issue and validate authorization tokens to make sure your services communicate in an authenticated and secure manner.
What are JWTs ?
JWT, or JSON Web Tokens, are tokens that are signed by a central authority that encapsulate authorization information. This website can help debugging your tokens.
A JWT is comprised of 3 parts
- Header
- Payload
- Signature
The header gives you a bunch of infos about the algorithm used, the key id
used to sign the token and so on. The payload is the actual encoded auth data that you care about and the signature is used to validate the token.
Setup Vault
We are going to demonstrate that with a dev vault, so first start a vault server in a separate terminal.
|
|
In another terminal
|
|
Create a Vault policy
We will need to create a policy to allow the account (that we will create right after) to perform some basic operations on Vault. For the purpose of this article we are going to create a read only policy on the whole Vault. You obviously do not want to do that in an actual production environment.
|
|
Create the OIDC issuer
To create the OIDC issuer, do
|
|
This will be used to populate the issuer
field of your tokens.
You will need then to create a key to sign your tokens:
|
|
Alright now we had a key that will sign our tokens. Note that in a real production environment you will need to have a key per environment (dev/staging/prod and so on) and will need to individually allow client ID (which we talk about later) to be signed by your key.
You then need to create something called a role
in Vault. Which will map to the app you want to authenticate against. In this example we will assume that our app is called demo
, you will have to create it as follows (so it is signed with the key
created above):
|
|
Good! No we need to create a user to authenticate.
Create the AppRole
An AppRole is a Vault authentication backend. You can see it as something similar to a username/password authentication, but intended for services instead of actual human users.
Enable the approle
authentication backend:
|
|
Now create the actual approle
, it will be called demo-approle
:
|
|
Then you will need to get two pieces of information, the roleid
and the secretid
for the approle. These are the equivalent of the username and the password to authenticate yourself.
|
|
Your values will be different.
Create the entity and map it to the AppRole
Now that we have created the approle, we need to map it to an internal Vault entity
, you need to do that because several entities
can be mapped to various authentication backends, like userpass
or if you use something like Google or what not. So first, create the entity and save it for later:
|
|
Now you finally need to create an entity alias
to make the link between the entity
and the approle
authentication backend (that is tedious I know but bear with me i swear it is worth it). Retrieve the accessor
, which is the internal Vault reference to your approle authentication backend:
|
|
Now finally (y e s f i n a l l y
) create the alias:
|
|
Aight. Everything is setup now.
Log in as the AppRole
Now all you need to do is to log into Vault using the approle, then issue a token:
|
|
You are now logged into Vault as your approle ! Check it by running:
|
|
Issue a token
Finally you can issue a token:
|
|
You can now use this token to identify to a service !
Let’s unpack the token a bit using the debugger. The headers read
|
|
The is not much about it, it specifies the signature algorithm used and the key id
used to sign the token, more on that later.
The body of the token reads the following:
|
|
Here you have a bunch of infos about the identity of the token bearer:
exp
is the expiration time of the tokeniat
is the issuance timeiss
is the issueraud
is the intended audience of the token, namely thedemo
OIDC role you created abovesub
is thesubject
of the token, namely the identity of the bearer. If you pay attention, this is the same UUID as the one referenced in theentity_id
field of thevault token lookup
command.
You can now identify who’s token you are looking at !
If you use Vault, you can also add more custom fields, such as group membership and other arbitrary things, more info on that here.
Verifying the tokens
Now you need to be able to verify the tokens. I will not expand on how to do the authorization, that’s your logic, and your problem, same for the expiration and issuer verification. However you need to be able to verify the signature of the token to establish that the token:
- Comes from whom it says it comes from
- Is signed by a key owned by whom it says it comes from
Vault exposes an unauthenticated endpoint that allows you to retrieve the public part of the signing keys used for the tokens, which you can access the following way
|
|
If you pay attention and fluently speak UUID, you will obviously notice that 962cbe97-f3ca-15c3-042d-61e431c194e0
is the kid
present in the header of the token we have previously issued.
This way you can verify that the signature is valid. Note that Vault implements the openID discovery protocol which can give you access to even more information.
Wrap up
I hope that will be useful to you to use Vault as an OIDC provider for your services ! :)