Vault as an OAuth/OIDC Identity Provider

Good Evening. Can vault can be used as an OAuth identity provider. It appears that it can by the documentation, however it is a little vague, so I just wanted to be sure. My idea is to integrate it with spring security’s oauth implementation so I can have users authenticate via vault and use it just like any other oauth provider (ex: google/github/etc).

The specific documentation pages I’m referring to are located here:


Thanks for all your great work. I love Hashicorp’s products.

I couldn’t find any page saying that Vault can act as an OAuth or OpenIDC provider.

However, Vault can issue OIDC identity tokens:

Unfortunately, I don’t think this by itself is sufficient to turn Vault into an OpenID Connect provider (in the style of Dex IDP or Keycloak), although it could be a building block.

Firstly, the OIDC provider needs to act as an endpoint for the OIDC protocol exchange, rather than the Vault REST API.

Secondly, the user has to authenticate themselves to Vault, and then use their Vault token to issue an OIDC token. In the simplest case, an OIDC frontend could display a webpage asking for username and password, and use the userpass secrets engine to get a vault token for that user. Making this properly secure would require some form of 2FA.

Thirdly, the OIDC provider needs to store a client_id and client_secret for each OIDC client (although I note that identity/oidc/role already includes a client_id. Maybe the role name is really the client_id, and the client_id is really the client_secret? Except that allowed_client_ids in the key contradicts that)

And fourthly, the OIDC provider must include additional claims in the JWT, limited to the scopes which the client requested, and which the user has explicitly granted permission for. The endpoint which issues tokens doesn’t seem to have a way to control claims, although the role includes a “template string to use for generating tokens” (without further explanation).

If I decode the sample token shown in the API response at jsonwebtoken.io, I get:

# Header
{
 "typ": "JWT",
 "alg": "RS256",
 "kid": "2d0b8b9d-f04d-71ec-b674-c7358432bc5b"
}

# Payload
{
 "aud": "P6CfCzyHsQY4pMcA6kWAOCItA7",
 "exp": 1561488412,
 "iat": 1561402012,
 "iss": "https://example.com:1234",
 "sub": "6c65eaf7-d4f4-1333-02bc-1c75219c3102"
}

As far as I can see, this is the most important missing piece: the ability to map entity metadata to JWT claims, and to control which claims are included on a token-by-token basis. (This includes mapping Vault groups to JWT group claims).

If that existed, so that Vault could become a general OIDC provider, that would indeed be awesome!

The ID token templating is documented here and I found the source here. According to the test cases, you should be able to map entity metadata, and add claims such as

"groups": {{identity.entity.groups.names}}

or

"groups": {{identity.entity.groups.ids}}

I’ve tested this:

vault write identity/oidc/key/oidckey - <<'EOH'
{
  "allowed_client_ids": "*"
}
EOH

vault write identity/oidc/role/openid_profile - <<"EOH"
{
  "key": "oidckey",
  "template": "{\"foo\":\"bar\",\"groups\":{{identity.entity.groups.names}}}"
}
EOH

# Policy to allow issuing tokens
vault policy write oidc_token - <<"EOH"
path "identity/oidc/token/*" {
  capabilities = ["read"]
}
EOH

vault auth enable userpass
vault write auth/userpass/users/example password=tcpip123 policies=oidc_token
# note the entity id and add to two new groups:
vault write identity/group name="engineers" member_entity_ids=c930e32a-493e-90e4-226a-d8ef8506e2e8
vault write identity/group name="staff" member_entity_ids=c930e32a-493e-90e4-226a-d8ef8506e2e8

# Login as this user and issue token
vault login -method=userpass username=example password=tcpip123
vault read identity/oidc/token/openid_profile

Resulting token decodes as:

{
 "aud": "2PuGzZuAbgqcaME5FMmxAp4dR8",
 "exp": 1613997204,
 "foo": "bar",
 "groups": [
  "engineers",
  "staff"
 ],
 "iat": 1613910804,
 "iss": "http://10.12.255.56:8200/v1/identity/oidc",
 "namespace": "root",
 "sub": "c930e32a-493e-90e4-226a-d8ef8506e2e8"
}

This is very impressive. I think it would require only a thin layer on top of this to act as OpenIDC provider.

As far as I can see, it does mean that you need to define a separate role for every unique combination of oidc scopes that you’re prepared to accept - but if you have only a few combinations, like openid and openid profile, then you’re fine. I also haven’t looked into how refresh tokens might work, but I presume they’d take the form of Vault tokens.

Making Vault a full OIDC provider sounds out of scope for what it really is - a secrets management solution, rather than an identity/user management system. However that doesn’t stop you using Vault as part of such a solution, with something like LDAP to handle the user administration and Vault for the tokens.

BTW, I had a go at making a stateless OIDC provider frontend to Vault - I even got as far as doing the full password grant flow.

There is, however, a fundamental problem when it comes to code grant. The relying party may include a nonce in the authentication request, and if they do, the generated JWT must also contain that nonce. But Vault’s token identity endpoint doesn’t provide a way to insert a nonce. If you insert it yourself, of course you invalidate the JWT signature.

It’s also rather inconvenient that every token signing role in Vault contains a fixed client ID. This means that you have to create a separate role for each openID client. Worse, if you want to support different sets of scopes (e.g. “openid”, “openid email”, “openid email profile”) then you need separate roles for those, with different templates, multiplied by the number of clients.

In the end, I think a more practical approach is to use Vault as a backend for a separate OpenID provider which does its own token signing. I’ve done this for Dex: the patch is here. It supports Vault for both OpenID and userpass authentication.

Unfortunately, Dex doesn’t yet have any concept of custom claims other than groups, so if you want to map Vault metadata into claims, they have to appear as groups. (Dex may in future gain custom claims as part of the work on middleware).

FYI, Vault 1.9.0 includes:

  • OIDC Provider (Tech Preview) : Vault can now act as an OIDC provider. Applications that support OIDC can now delegate authentication and authorization to Vault using OIDC.

Documentation here, here and here.

I am looking forward to trying this out!

EDIT: I have tried it - it works fine with Apache mod_auth_openidc as the relying party. I found a minor issue with OIDC spec compatibility which will be fixed in v1.9.1. I had some thoughts about the way access is limited to client_ids.

But from what I’ve seen, you get an out-of-the-box authorization code flow for OpenIDC, meaning you can have a single source of truth for web logins, SSH certificates, X509 certificates etc. Very nice indeed!

And don’t forget the learn platform:

2 Likes

I’ve made a writeup here:

1 Like